diff mbox series

C++ PATCH to implement P1064R0, Virtual Function Calls in Constant Expressions

Message ID 20180914171950.GE5587@redhat.com
State New
Headers show
Series C++ PATCH to implement P1064R0, Virtual Function Calls in Constant Expressions | expand

Commit Message

Marek Polacek Sept. 14, 2018, 5:19 p.m. UTC
This patch implements another bit of C++20, virtual calls in constant
expression:
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
The basic idea is that since in a constant expression we know the dynamic
type (to detect invalid code etc.), the restriction that prohibits virtual
calls is unnecessary.

Handling virtual function calls turned out to be fairly easy (as anticipated);
I simply let the constexpr machinery figure out the dynamic type and then
OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
way we get the function decl we're interested in, and cxx_eval_call_expression
takes it from there.

But handling pointer-to-virtual-member-functions doesn't work like that.
get_member_function_from_ptrfunc creates a COND_EXPR which looks like
if (pf.__pfn & 1) // is it a virtual function?
  // yes, find the pointer in the vtable
else
  // no, just return the pointer
so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
with "not a constant expression" error.  Since the vtable initializer is
a compile-time constant, I thought we could make it work by a hack as the one
in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
job and everything is hunky-dory.

Except when it isn't: I noticed that the presence of _vptr doesn't make the
class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
class type, it just evaluated it to { }.  But such a class still had gotten an
initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
replacing it with { } will lose the proper initializer whereupon we fail.
The check I've added to cxx_eval_constant_expression won't win any beauty
contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.

I've made no attempt to handle virtual bases.  The proposal doesn't mention
them at all, and I thought this would be enough for starters.

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.

Comments

Jakub Jelinek Sept. 14, 2018, 5:36 p.m. UTC | #1
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?

	Jakub
Marek Polacek Sept. 14, 2018, 7:30 p.m. UTC | #2
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();
    ~~~~~~~~~ ^~~~~~~
Jason Merrill Sept. 14, 2018, 8:30 p.m. UTC | #3
On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote:
> This patch implements another bit of C++20, virtual calls in constant
> expression:
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
> The basic idea is that since in a constant expression we know the dynamic
> type (to detect invalid code etc.), the restriction that prohibits virtual
> calls is unnecessary.
>
> Handling virtual function calls turned out to be fairly easy (as anticipated);
> I simply let the constexpr machinery figure out the dynamic type and then
> OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
> way we get the function decl we're interested in, and cxx_eval_call_expression
> takes it from there.
>
> But handling pointer-to-virtual-member-functions doesn't work like that.
> get_member_function_from_ptrfunc creates a COND_EXPR which looks like
> if (pf.__pfn & 1) // is it a virtual function?
>   // yes, find the pointer in the vtable
> else
>   // no, just return the pointer
> so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
> to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
> with "not a constant expression" error.

Then let's mark the vtable as constexpr, there's no reason for it not to be.

> Since the vtable initializer is
> a compile-time constant, I thought we could make it work by a hack as the one
> in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
> job and everything is hunky-dory.
>
> Except when it isn't: I noticed that the presence of _vptr doesn't make the
> class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
> class type, it just evaluated it to { }.  But such a class still had gotten an
> initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
> replacing it with { } will lose the proper initializer whereupon we fail.
> The check I've added to cxx_eval_constant_expression won't win any beauty
> contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.

Perhaps we should check !TYPE_POLYMORPHIC_P as well as
is_really_empty_class.  Perhaps there should be a predicate for that,
say, is_really_nearly_empty_class...

Jason
Marek Polacek Sept. 14, 2018, 8:45 p.m. UTC | #4
On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote:
> On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote:
> > This patch implements another bit of C++20, virtual calls in constant
> > expression:
> > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
> > The basic idea is that since in a constant expression we know the dynamic
> > type (to detect invalid code etc.), the restriction that prohibits virtual
> > calls is unnecessary.
> >
> > Handling virtual function calls turned out to be fairly easy (as anticipated);
> > I simply let the constexpr machinery figure out the dynamic type and then
> > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
> > way we get the function decl we're interested in, and cxx_eval_call_expression
> > takes it from there.
> >
> > But handling pointer-to-virtual-member-functions doesn't work like that.
> > get_member_function_from_ptrfunc creates a COND_EXPR which looks like
> > if (pf.__pfn & 1) // is it a virtual function?
> >   // yes, find the pointer in the vtable
> > else
> >   // no, just return the pointer
> > so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
> > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
> > with "not a constant expression" error.
> 
> Then let's mark the vtable as constexpr, there's no reason for it not to be.

Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P
in initialize_artificial_var because then I saw "used in its own initializer"
error.  Which I don't know why, but now that I know you agree with this direction
I can dig deeper.
 
> > Since the vtable initializer is
> > a compile-time constant, I thought we could make it work by a hack as the one
> > in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
> > job and everything is hunky-dory.
> >
> > Except when it isn't: I noticed that the presence of _vptr doesn't make the
> > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
> > class type, it just evaluated it to { }.  But such a class still had gotten an
> > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
> > replacing it with { } will lose the proper initializer whereupon we fail.
> > The check I've added to cxx_eval_constant_expression won't win any beauty
> > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.
> 
> Perhaps we should check !TYPE_POLYMORPHIC_P as well as
> is_really_empty_class.  Perhaps there should be a predicate for that,
> say, is_really_nearly_empty_class...

Ack.  Thanks,

Marek
Marek Polacek Sept. 17, 2018, 9:39 p.m. UTC | #5
On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote:
> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote:
> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote:
> > > This patch implements another bit of C++20, virtual calls in constant
> > > expression:
> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
> > > The basic idea is that since in a constant expression we know the dynamic
> > > type (to detect invalid code etc.), the restriction that prohibits virtual
> > > calls is unnecessary.
> > >
> > > Handling virtual function calls turned out to be fairly easy (as anticipated);
> > > I simply let the constexpr machinery figure out the dynamic type and then
> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
> > > way we get the function decl we're interested in, and cxx_eval_call_expression
> > > takes it from there.
> > >
> > > But handling pointer-to-virtual-member-functions doesn't work like that.
> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like
> > > if (pf.__pfn & 1) // is it a virtual function?
> > >   // yes, find the pointer in the vtable
> > > else
> > >   // no, just return the pointer
> > > so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
> > > with "not a constant expression" error.
> > 
> > Then let's mark the vtable as constexpr, there's no reason for it not to be.

Done.  But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt
expects it), which broke an assert in gimple_get_virt_method_for_vtable.  But I
don't need the array_ref hack anymore!

Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var,
otherwise we run into the sorry in that function with -fno-weak...

> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P
> in initialize_artificial_var because then I saw "used in its own initializer"
> error.  Which I don't know why, but now that I know you agree with this direction
> I can dig deeper.
>  
> > > Since the vtable initializer is
> > > a compile-time constant, I thought we could make it work by a hack as the one
> > > in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
> > > job and everything is hunky-dory.
> > >
> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the
> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
> > > class type, it just evaluated it to { }.  But such a class still had gotten an
> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
> > > replacing it with { } will lose the proper initializer whereupon we fail.
> > > The check I've added to cxx_eval_constant_expression won't win any beauty
> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.
> > 
> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as
> > is_really_empty_class.  Perhaps there should be a predicate for that,
> > say, is_really_nearly_empty_class...

For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-09-17  Marek Polacek  <polacek@redhat.com>

	P1064R0 - Allowing Virtual Function Calls in Constant Expressions
	* call.c (build_over_call): Add FIXME.
	* class.c (initialize_vtable): Mark the vtable as constexpr.
	(build_vtbl_initializer): Build vtable's constructor with indexes.
	* constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
	initializer.  Handle OBJ_TYPE_REF.
	(potential_constant_expression_1): Handle OBJ_TYPE_REF.
	* decl.c (grokdeclarator): Change error to pedwarn.  Only warn when
	pedantic and not C++2a.

	* gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert.

	* 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/class.c gcc/cp/class.c
index e950a7423f7..288033fe912 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -8681,6 +8681,10 @@ initialize_vtable (tree binfo, vec<constructor_elt, va_gc> *inits)
   layout_vtable_decl (binfo, vec_safe_length (inits));
   decl = get_vtbl_decl_for_binfo (binfo);
   initialize_artificial_var (decl, inits);
+  /* Mark the vtable as constexpr so that we can access its content
+     at compile time.  */
+  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
+  DECL_DECLARED_CONSTEXPR_P (decl) = true;
   dump_vtable (BINFO_TYPE (binfo), binfo, decl);
 }
 
@@ -9266,6 +9270,7 @@ build_vtbl_initializer (tree binfo,
       tree vcall_index;
       tree fn, fn_original;
       tree init = NULL_TREE;
+      tree idx = build_int_cst (size_type_node, jx++);
 
       fn = BV_FN (v);
       fn_original = fn;
@@ -9369,7 +9374,7 @@ build_vtbl_initializer (tree binfo,
 	  int i;
 	  if (init == size_zero_node)
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
-	      CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
+	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
 	  else
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
 	      {
@@ -9377,11 +9382,11 @@ build_vtbl_initializer (tree binfo,
 				     fn, build_int_cst (NULL_TREE, i));
 		TREE_CONSTANT (fdesc) = 1;
 
-		CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
+		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
 	      }
 	}
       else
-	CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
+	CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
     }
 }
 
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 88c73787961..aa33319875f 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4209,7 +4209,11 @@ 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))
+	  && !TYPE_POLYMORPHIC_P (TREE_TYPE (t))
 	  && is_really_empty_class (TREE_TYPE (t)))
 	{
 	  /* If the class is empty, we aren't actually loading anything.  */
@@ -4778,7 +4782,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 +4791,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 +5902,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 +5910,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/gimple-fold.c gcc/gimple-fold.c
index 362ab59e9c0..dd30a30b8c5 100644
--- gcc/gimple-fold.c
+++ gcc/gimple-fold.c
@@ -6988,12 +6988,11 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
   access_index = offset / BITS_PER_UNIT / elt_size;
   gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0);
 
-  /* This code makes an assumption that there are no 
-     indexed fileds produced by C++ FE, so we can directly index the array. */
+  /* The C++ FE now produces indexed fields but we can index the array
+     directly. */
   if (access_index < CONSTRUCTOR_NELTS (init))
     {
       fn = CONSTRUCTOR_ELT (init, access_index)->value;
-      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
       STRIP_NOPS (fn);
     }
   else
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();
    ~~~~~~~~~ ^~~~~~~
Jason Merrill Sept. 18, 2018, 3:28 a.m. UTC | #6
On Mon, Sep 17, 2018 at 5:39 PM, Marek Polacek <polacek@redhat.com> wrote:
> On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote:
>> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote:
>> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote:
>> > > This patch implements another bit of C++20, virtual calls in constant
>> > > expression:
>> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
>> > > The basic idea is that since in a constant expression we know the dynamic
>> > > type (to detect invalid code etc.), the restriction that prohibits virtual
>> > > calls is unnecessary.
>> > >
>> > > Handling virtual function calls turned out to be fairly easy (as anticipated);
>> > > I simply let the constexpr machinery figure out the dynamic type and then
>> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
>> > > way we get the function decl we're interested in, and cxx_eval_call_expression
>> > > takes it from there.
>> > >
>> > > But handling pointer-to-virtual-member-functions doesn't work like that.
>> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like
>> > > if (pf.__pfn & 1) // is it a virtual function?
>> > >   // yes, find the pointer in the vtable
>> > > else
>> > >   // no, just return the pointer
>> > > so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
>> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
>> > > with "not a constant expression" error.
>> >
>> > Then let's mark the vtable as constexpr, there's no reason for it not to be.
>
> Done.  But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt
> expects it), which broke an assert in gimple_get_virt_method_for_vtable.  But I
> don't need the array_ref hack anymore!

> Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var,
> otherwise we run into the sorry in that function with -fno-weak...

Hmm, we shouldn't give that sorry for DECL_ARTIFICIAL variables.

Looking more closely, it seems that the call to maybe_commonize_var
from initialize_artificial_var did nothing before this change, since
the vtable is DECL_ARTIFICIAL, so it didn't pass the condition at the
top.  I suppose we should extend the !DECL_ARTIFICIAL check in
maybe_commonize_var to the inline variable case as well.

>> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P
>> in initialize_artificial_var because then I saw "used in its own initializer"
>> error.  Which I don't know why, but now that I know you agree with this direction
>> I can dig deeper.
>>
>> > > Since the vtable initializer is
>> > > a compile-time constant, I thought we could make it work by a hack as the one
>> > > in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
>> > > job and everything is hunky-dory.
>> > >
>> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the
>> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
>> > > class type, it just evaluated it to { }.  But such a class still had gotten an
>> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
>> > > replacing it with { } will lose the proper initializer whereupon we fail.
>> > > The check I've added to cxx_eval_constant_expression won't win any beauty
>> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.
>> >
>> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as
>> > is_really_empty_class.  Perhaps there should be a predicate for that,
>> > say, is_really_nearly_empty_class...
>
> For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2018-09-17  Marek Polacek  <polacek@redhat.com>
>
>         P1064R0 - Allowing Virtual Function Calls in Constant Expressions
>         * call.c (build_over_call): Add FIXME.
>         * class.c (initialize_vtable): Mark the vtable as constexpr.
>         (build_vtbl_initializer): Build vtable's constructor with indexes.
>         * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
>         initializer.  Handle OBJ_TYPE_REF.
>         (potential_constant_expression_1): Handle OBJ_TYPE_REF.
>         * decl.c (grokdeclarator): Change error to pedwarn.  Only warn when
>         pedantic and not C++2a.
>
>         * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert.
>
>         * 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 ())

I notice that removing the in_template_function check doesn't break
template/virtual4.C nowadays.  Does it break anything else?

> +  /* The C++ FE now produces indexed fields but we can index the array
> +     directly. */
>    if (access_index < CONSTRUCTOR_NELTS (init))
>      {
>        fn = CONSTRUCTOR_ELT (init, access_index)->value;
> -      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);

Rather than remove this assert, let's fix it to check that the ->index
matches access_index.

Jason
Marek Polacek Sept. 18, 2018, 3:25 p.m. UTC | #7
On Mon, Sep 17, 2018 at 11:28:06PM -0400, Jason Merrill wrote:
> On Mon, Sep 17, 2018 at 5:39 PM, Marek Polacek <polacek@redhat.com> wrote:
> > On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote:
> >> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote:
> >> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote:
> >> > > This patch implements another bit of C++20, virtual calls in constant
> >> > > expression:
> >> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
> >> > > The basic idea is that since in a constant expression we know the dynamic
> >> > > type (to detect invalid code etc.), the restriction that prohibits virtual
> >> > > calls is unnecessary.
> >> > >
> >> > > Handling virtual function calls turned out to be fairly easy (as anticipated);
> >> > > I simply let the constexpr machinery figure out the dynamic type and then
> >> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
> >> > > way we get the function decl we're interested in, and cxx_eval_call_expression
> >> > > takes it from there.
> >> > >
> >> > > But handling pointer-to-virtual-member-functions doesn't work like that.
> >> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like
> >> > > if (pf.__pfn & 1) // is it a virtual function?
> >> > >   // yes, find the pointer in the vtable
> >> > > else
> >> > >   // no, just return the pointer
> >> > > so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
> >> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
> >> > > with "not a constant expression" error.
> >> >
> >> > Then let's mark the vtable as constexpr, there's no reason for it not to be.
> >
> > Done.  But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt
> > expects it), which broke an assert in gimple_get_virt_method_for_vtable.  But I
> > don't need the array_ref hack anymore!
> 
> > Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var,
> > otherwise we run into the sorry in that function with -fno-weak...
> 
> Hmm, we shouldn't give that sorry for DECL_ARTIFICIAL variables.
> 
> Looking more closely, it seems that the call to maybe_commonize_var
> from initialize_artificial_var did nothing before this change, since
> the vtable is DECL_ARTIFICIAL, so it didn't pass the condition at the
> top.  I suppose we should extend the !DECL_ARTIFICIAL check in
> maybe_commonize_var to the inline variable case as well.

Done.  And then I could move setting DECL_DECLARED_CONSTEXPR_P to
initialize_artificial_var, which is where I think it belongs.

> >> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P
> >> in initialize_artificial_var because then I saw "used in its own initializer"
> >> error.  Which I don't know why, but now that I know you agree with this direction
> >> I can dig deeper.
> >>
> >> > > Since the vtable initializer is
> >> > > a compile-time constant, I thought we could make it work by a hack as the one
> >> > > in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
> >> > > job and everything is hunky-dory.
> >> > >
> >> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the
> >> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
> >> > > class type, it just evaluated it to { }.  But such a class still had gotten an
> >> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
> >> > > replacing it with { } will lose the proper initializer whereupon we fail.
> >> > > The check I've added to cxx_eval_constant_expression won't win any beauty
> >> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.
> >> >
> >> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as
> >> > is_really_empty_class.  Perhaps there should be a predicate for that,
> >> > say, is_really_nearly_empty_class...
> >
> > For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine.
> >
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> >
> > 2018-09-17  Marek Polacek  <polacek@redhat.com>
> >
> >         P1064R0 - Allowing Virtual Function Calls in Constant Expressions
> >         * call.c (build_over_call): Add FIXME.
> >         * class.c (initialize_vtable): Mark the vtable as constexpr.
> >         (build_vtbl_initializer): Build vtable's constructor with indexes.
> >         * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
> >         initializer.  Handle OBJ_TYPE_REF.
> >         (potential_constant_expression_1): Handle OBJ_TYPE_REF.
> >         * decl.c (grokdeclarator): Change error to pedwarn.  Only warn when
> >         pedantic and not C++2a.
> >
> >         * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert.
> >
> >         * 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 ())
> 
> I notice that removing the in_template_function check doesn't break
> template/virtual4.C nowadays.  Does it break anything else?

It doesn't seem to!  So, removed.

> > +  /* The C++ FE now produces indexed fields but we can index the array
> > +     directly. */
> >    if (access_index < CONSTRUCTOR_NELTS (init))
> >      {
> >        fn = CONSTRUCTOR_ELT (init, access_index)->value;
> > -      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
> 
> Rather than remove this assert, let's fix it to check that the ->index
> matches access_index.

Done.

Bootstrapped/regtested on ppc64le-linux, additionally tested dg.exp with
GXX_TESTSUITE_STDS=98,11,14,17,2a on x86_64-linux, ok for trunk?

2018-09-18  Marek Polacek  <polacek@redhat.com>

	P1064R0 - Allowing Virtual Function Calls in Constant Expressions
	* call.c (build_over_call): No longer check if we're outside a template
	function.
	* class.c (build_vtbl_initializer): Build vtable's constructor with
	indexes.
	* constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
	initializer.  Handle OBJ_TYPE_REF.
	(potential_constant_expression_1): Handle OBJ_TYPE_REF.
	* decl.c (maybe_commonize_var): Bail out for any DECL_ARTIFICIAL.
	(initialize_artificial_var): Mark the variable as constexpr.
	(grokdeclarator): Change error to pedwarn.  Only warn when
	pedantic and not C++2a.

	* gimple-fold.c (gimple_get_virt_method_for_vtable): Adjust assert.

	* 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..ddf0ed044a0 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -8399,10 +8399,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL)
     maybe_warn_class_memaccess (input_location, fn, args);
 
-  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.  */
-      && !in_template_function ())
+  if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0)
     {
       tree t;
       tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])),
diff --git gcc/cp/class.c gcc/cp/class.c
index e950a7423f7..b9cde037337 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo,
       tree vcall_index;
       tree fn, fn_original;
       tree init = NULL_TREE;
+      tree idx = build_int_cst (size_type_node, jx++);
 
       fn = BV_FN (v);
       fn_original = fn;
@@ -9369,7 +9370,7 @@ build_vtbl_initializer (tree binfo,
 	  int i;
 	  if (init == size_zero_node)
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
-	      CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
+	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
 	  else
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
 	      {
@@ -9377,11 +9378,11 @@ build_vtbl_initializer (tree binfo,
 				     fn, build_int_cst (NULL_TREE, i));
 		TREE_CONSTANT (fdesc) = 1;
 
-		CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
+		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
 	      }
 	}
       else
-	CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
+	CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
     }
 }
 
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 88c73787961..aa33319875f 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4209,7 +4209,11 @@ 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))
+	  && !TYPE_POLYMORPHIC_P (TREE_TYPE (t))
 	  && is_really_empty_class (TREE_TYPE (t)))
 	{
 	  /* If the class is empty, we aren't actually loading anything.  */
@@ -4778,7 +4782,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 +4791,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 +5902,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 +5910,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..827c1720335 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5583,11 +5583,13 @@ layout_var_decl (tree decl)
 void
 maybe_commonize_var (tree decl)
 {
+  /* Don't mess with __FUNCTION__ and similar.  */
+  if (DECL_ARTIFICIAL (decl))
+    return;
+
   /* Static data in a function with comdat linkage also has comdat
      linkage.  */
   if ((TREE_STATIC (decl)
-       /* Don't mess with __FUNCTION__.  */
-       && ! DECL_ARTIFICIAL (decl)
        && DECL_FUNCTION_SCOPE_P (decl)
        && vague_linkage_p (DECL_CONTEXT (decl)))
       || (TREE_PUBLIC (decl) && DECL_INLINE_VAR_P (decl)))
@@ -6774,6 +6776,10 @@ initialize_artificial_var (tree decl, vec<constructor_elt, va_gc> *v)
   gcc_assert (TREE_CODE (init) == CONSTRUCTOR);
   DECL_INITIAL (decl) = init;
   DECL_INITIALIZED_P (decl) = 1;
+  /* Mark the decl as constexpr so that we can access its content
+     at compile time.  */
+  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
+  DECL_DECLARED_CONSTEXPR_P (decl) = true;
   determine_visibility (decl);
   layout_var_decl (decl);
   maybe_commonize_var (decl);
@@ -10854,12 +10860,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/gimple-fold.c gcc/gimple-fold.c
index 362ab59e9c0..1e84722d22d 100644
--- gcc/gimple-fold.c
+++ gcc/gimple-fold.c
@@ -6988,12 +6988,13 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
   access_index = offset / BITS_PER_UNIT / elt_size;
   gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0);
 
-  /* This code makes an assumption that there are no 
-     indexed fileds produced by C++ FE, so we can directly index the array. */
+  /* The C++ FE can now produce indexed fields, and we check if the indexes
+     match.  */
   if (access_index < CONSTRUCTOR_NELTS (init))
     {
       fn = CONSTRUCTOR_ELT (init, access_index)->value;
-      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
+      tree idx = CONSTRUCTOR_ELT (init, access_index)->index;
+      gcc_checking_assert (!idx || tree_to_uhwi (idx) == access_index);
       STRIP_NOPS (fn);
     }
   else
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();
    ~~~~~~~~~ ^~~~~~~
Jason Merrill Sept. 18, 2018, 6:29 p.m. UTC | #8
On Tue, Sep 18, 2018 at 11:25 AM, Marek Polacek <polacek@redhat.com> wrote:
> On Mon, Sep 17, 2018 at 11:28:06PM -0400, Jason Merrill wrote:
>> On Mon, Sep 17, 2018 at 5:39 PM, Marek Polacek <polacek@redhat.com> wrote:
>> > On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote:
>> >> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote:
>> >> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote:
>> >> > > This patch implements another bit of C++20, virtual calls in constant
>> >> > > expression:
>> >> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html>
>> >> > > The basic idea is that since in a constant expression we know the dynamic
>> >> > > type (to detect invalid code etc.), the restriction that prohibits virtual
>> >> > > calls is unnecessary.
>> >> > >
>> >> > > Handling virtual function calls turned out to be fairly easy (as anticipated);
>> >> > > I simply let the constexpr machinery figure out the dynamic type and then
>> >> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table.  That
>> >> > > way we get the function decl we're interested in, and cxx_eval_call_expression
>> >> > > takes it from there.
>> >> > >
>> >> > > But handling pointer-to-virtual-member-functions doesn't work like that.
>> >> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like
>> >> > > if (pf.__pfn & 1) // is it a virtual function?
>> >> > >   // yes, find the pointer in the vtable
>> >> > > else
>> >> > >   // no, just return the pointer
>> >> > > so ideally we want to evaluate the then-branch.  Eventually it'll evaluate it
>> >> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up
>> >> > > with "not a constant expression" error.
>> >> >
>> >> > Then let's mark the vtable as constexpr, there's no reason for it not to be.
>> >
>> > Done.  But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt
>> > expects it), which broke an assert in gimple_get_virt_method_for_vtable.  But I
>> > don't need the array_ref hack anymore!
>>
>> > Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var,
>> > otherwise we run into the sorry in that function with -fno-weak...
>>
>> Hmm, we shouldn't give that sorry for DECL_ARTIFICIAL variables.
>>
>> Looking more closely, it seems that the call to maybe_commonize_var
>> from initialize_artificial_var did nothing before this change, since
>> the vtable is DECL_ARTIFICIAL, so it didn't pass the condition at the
>> top.  I suppose we should extend the !DECL_ARTIFICIAL check in
>> maybe_commonize_var to the inline variable case as well.
>
> Done.  And then I could move setting DECL_DECLARED_CONSTEXPR_P to
> initialize_artificial_var, which is where I think it belongs.
>
>> >> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P
>> >> in initialize_artificial_var because then I saw "used in its own initializer"
>> >> error.  Which I don't know why, but now that I know you agree with this direction
>> >> I can dig deeper.
>> >>
>> >> > > Since the vtable initializer is
>> >> > > a compile-time constant, I thought we could make it work by a hack as the one
>> >> > > in cxx_eval_array_reference.  We'll then let cxx_eval_call_expression do its
>> >> > > job and everything is hunky-dory.
>> >> > >
>> >> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the
>> >> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty
>> >> > > class type, it just evaluated it to { }.  But such a class still had gotten an
>> >> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}.  So
>> >> > > replacing it with { } will lose the proper initializer whereupon we fail.
>> >> > > The check I've added to cxx_eval_constant_expression won't win any beauty
>> >> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there.
>> >> >
>> >> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as
>> >> > is_really_empty_class.  Perhaps there should be a predicate for that,
>> >> > say, is_really_nearly_empty_class...
>> >
>> > For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine.
>> >
>> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
>> >
>> > 2018-09-17  Marek Polacek  <polacek@redhat.com>
>> >
>> >         P1064R0 - Allowing Virtual Function Calls in Constant Expressions
>> >         * call.c (build_over_call): Add FIXME.
>> >         * class.c (initialize_vtable): Mark the vtable as constexpr.
>> >         (build_vtbl_initializer): Build vtable's constructor with indexes.
>> >         * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
>> >         initializer.  Handle OBJ_TYPE_REF.
>> >         (potential_constant_expression_1): Handle OBJ_TYPE_REF.
>> >         * decl.c (grokdeclarator): Change error to pedwarn.  Only warn when
>> >         pedantic and not C++2a.
>> >
>> >         * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert.
>> >
>> >         * 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 ())
>>
>> I notice that removing the in_template_function check doesn't break
>> template/virtual4.C nowadays.  Does it break anything else?
>
> It doesn't seem to!  So, removed.
>
>> > +  /* The C++ FE now produces indexed fields but we can index the array
>> > +     directly. */
>> >    if (access_index < CONSTRUCTOR_NELTS (init))
>> >      {
>> >        fn = CONSTRUCTOR_ELT (init, access_index)->value;
>> > -      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
>>
>> Rather than remove this assert, let's fix it to check that the ->index
>> matches access_index.
>
> Done.
>
> Bootstrapped/regtested on ppc64le-linux, additionally tested dg.exp with
> GXX_TESTSUITE_STDS=98,11,14,17,2a on x86_64-linux, ok for trunk?
>
> 2018-09-18  Marek Polacek  <polacek@redhat.com>
>
>         P1064R0 - Allowing Virtual Function Calls in Constant Expressions
>         * call.c (build_over_call): No longer check if we're outside a template
>         function.
>         * class.c (build_vtbl_initializer): Build vtable's constructor with
>         indexes.
>         * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
>         initializer.  Handle OBJ_TYPE_REF.
>         (potential_constant_expression_1): Handle OBJ_TYPE_REF.
>         * decl.c (maybe_commonize_var): Bail out for any DECL_ARTIFICIAL.
>         (initialize_artificial_var): Mark the variable as constexpr.
>         (grokdeclarator): Change error to pedwarn.  Only warn when
>         pedantic and not C++2a.
>
>         * gimple-fold.c (gimple_get_virt_method_for_vtable): Adjust assert.
>
>         * 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..ddf0ed044a0 100644
> --- gcc/cp/call.c
> +++ gcc/cp/call.c
> @@ -8399,10 +8399,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
>        && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL)
>      maybe_warn_class_memaccess (input_location, fn, args);
>
> -  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.  */
> -      && !in_template_function ())
> +  if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0)
>      {
>        tree t;
>        tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])),
> diff --git gcc/cp/class.c gcc/cp/class.c
> index e950a7423f7..b9cde037337 100644
> --- gcc/cp/class.c
> +++ gcc/cp/class.c
> @@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo,
>        tree vcall_index;
>        tree fn, fn_original;
>        tree init = NULL_TREE;
> +      tree idx = build_int_cst (size_type_node, jx++);

Let's use size_int here, like in process_init_constructor_array.

OK with that change.

Jason
Marek Polacek Sept. 18, 2018, 6:55 p.m. UTC | #9
On Tue, Sep 18, 2018 at 02:29:16PM -0400, Jason Merrill wrote:
> > --- gcc/cp/class.c
> > +++ gcc/cp/class.c
> > @@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo,
> >        tree vcall_index;
> >        tree fn, fn_original;
> >        tree init = NULL_TREE;
> > +      tree idx = build_int_cst (size_type_node, jx++);
> 
> Let's use size_int here, like in process_init_constructor_array.

Done.
 
> OK with that change.

Thanks for quick reviews!  Next one: explicit(bool).

Applying to trunk.

2018-09-18  Marek Polacek  <polacek@redhat.com>

	P1064R0 - Allowing Virtual Function Calls in Constant Expressions
	* call.c (build_over_call): No longer check if we're outside a template
	function.
	* class.c (build_vtbl_initializer): Build vtable's constructor with
	indexes.
	* constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's
	initializer.  Handle OBJ_TYPE_REF.
	(potential_constant_expression_1): Handle OBJ_TYPE_REF.
	* decl.c (maybe_commonize_var): Bail out for any DECL_ARTIFICIAL.
	(initialize_artificial_var): Mark the variable as constexpr.
	(grokdeclarator): Change error to pedwarn.  Only warn when
	pedantic and not C++2a.

	* gimple-fold.c (gimple_get_virt_method_for_vtable): Adjust assert.

	* 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..ddf0ed044a0 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -8399,10 +8399,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL)
     maybe_warn_class_memaccess (input_location, fn, args);
 
-  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.  */
-      && !in_template_function ())
+  if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0)
     {
       tree t;
       tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])),
diff --git gcc/cp/class.c gcc/cp/class.c
index e950a7423f7..9ca46441871 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo,
       tree vcall_index;
       tree fn, fn_original;
       tree init = NULL_TREE;
+      tree idx = size_int (jx++);
 
       fn = BV_FN (v);
       fn_original = fn;
@@ -9369,7 +9370,7 @@ build_vtbl_initializer (tree binfo,
 	  int i;
 	  if (init == size_zero_node)
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
-	      CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
+	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
 	  else
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
 	      {
@@ -9377,11 +9378,11 @@ build_vtbl_initializer (tree binfo,
 				     fn, build_int_cst (NULL_TREE, i));
 		TREE_CONSTANT (fdesc) = 1;
 
-		CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
+		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
 	      }
 	}
       else
-	CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
+	CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
     }
 }
 
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 88c73787961..aa33319875f 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4209,7 +4209,11 @@ 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))
+	  && !TYPE_POLYMORPHIC_P (TREE_TYPE (t))
 	  && is_really_empty_class (TREE_TYPE (t)))
 	{
 	  /* If the class is empty, we aren't actually loading anything.  */
@@ -4778,7 +4782,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 +4791,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 +5902,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 +5910,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..827c1720335 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5583,11 +5583,13 @@ layout_var_decl (tree decl)
 void
 maybe_commonize_var (tree decl)
 {
+  /* Don't mess with __FUNCTION__ and similar.  */
+  if (DECL_ARTIFICIAL (decl))
+    return;
+
   /* Static data in a function with comdat linkage also has comdat
      linkage.  */
   if ((TREE_STATIC (decl)
-       /* Don't mess with __FUNCTION__.  */
-       && ! DECL_ARTIFICIAL (decl)
        && DECL_FUNCTION_SCOPE_P (decl)
        && vague_linkage_p (DECL_CONTEXT (decl)))
       || (TREE_PUBLIC (decl) && DECL_INLINE_VAR_P (decl)))
@@ -6774,6 +6776,10 @@ initialize_artificial_var (tree decl, vec<constructor_elt, va_gc> *v)
   gcc_assert (TREE_CODE (init) == CONSTRUCTOR);
   DECL_INITIAL (decl) = init;
   DECL_INITIALIZED_P (decl) = 1;
+  /* Mark the decl as constexpr so that we can access its content
+     at compile time.  */
+  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
+  DECL_DECLARED_CONSTEXPR_P (decl) = true;
   determine_visibility (decl);
   layout_var_decl (decl);
   maybe_commonize_var (decl);
@@ -10854,12 +10860,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/gimple-fold.c gcc/gimple-fold.c
index 362ab59e9c0..1e84722d22d 100644
--- gcc/gimple-fold.c
+++ gcc/gimple-fold.c
@@ -6988,12 +6988,13 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
   access_index = offset / BITS_PER_UNIT / elt_size;
   gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0);
 
-  /* This code makes an assumption that there are no 
-     indexed fileds produced by C++ FE, so we can directly index the array. */
+  /* The C++ FE can now produce indexed fields, and we check if the indexes
+     match.  */
   if (access_index < CONSTRUCTOR_NELTS (init))
     {
       fn = CONSTRUCTOR_ELT (init, access_index)->value;
-      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
+      tree idx = CONSTRUCTOR_ELT (init, access_index)->index;
+      gcc_checking_assert (!idx || tree_to_uhwi (idx) == access_index);
       STRIP_NOPS (fn);
     }
   else
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();
    ~~~~~~~~~ ^~~~~~~
Andreas Schwab Sept. 19, 2018, 1:25 p.m. UTC | #10
I'm getting this ICE on ia64:

libtool: compile:  /usr/local/gcc/gcc-20180919/Build/./gcc/xgcc -shared-libgcc -B/usr/local/gcc/gcc-20180919/Build/./gcc -nostdinc++ -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src/.libs -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/libsupc++/.libs -B/usr/ia64-suse-linux/bin/ -B/usr/ia64-suse-linux/lib/ -isystem /usr/ia64-suse-linux/include -isystem /usr/ia64-suse-linux/sys-include -fno-checking -I/usr/local/gcc/gcc-20180919/libstdc++-v3/../libgcc -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include/ia64-suse-linux -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include -I/usr/local/gcc/gcc-20180919/libstdc++-v3/libsupc++ -D_GLIBCXX_SHARED -fno-implicit-templates -Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi=2 -fdiagnostics-show-location=once -ffunction-sections -fdata-sections -frandom-seed=bad_typeid.lo -O2 -g -D_GNU_SOURCE -c ../../../../libstdc++-v3/libsupc++/bad_typeid.cc  -fPIC -DPIC -D_GLIBCXX_SHARED -o bad_typeid.o
../../../../libstdc++-v3/libsupc++/bad_typeid.cc:36:1: internal compiler error: in output_constructor_regular_field, at varasm.c:5165
36 | } // namespace std
   | ^
0x400000000176abaf output_constructor_regular_field
        ../../gcc/varasm.c:5165
0x400000000176d09f output_constructor
        ../../gcc/varasm.c:5475
0x400000000176940f output_constant
        ../../gcc/varasm.c:4967
0x400000000175414f assemble_variable_contents
        ../../gcc/varasm.c:2143
0x400000000175586f assemble_variable(tree_node*, int, int, int)
        ../../gcc/varasm.c:2319
0x40000000017a564f varpool_node::assemble_decl()
        ../../gcc/varpool.c:586
0x40000000017a74cf symbol_table::output_variables()
        ../../gcc/varpool.c:752
0x40000000007b806f symbol_table::compile()
        ../../gcc/cgraphunit.c:2611
0x40000000007bd8ef symbol_table::compile()
        ../../gcc/cgraphunit.c:2791
0x40000000007bd8ef symbol_table::finalize_compilation_unit()
        ../../gcc/cgraphunit.c:2788

(gdb) up
#1  0x400000000176abb0 in output_constructor_regular_field (
    local=0x600ffffffffee920) at ../../gcc/varasm.c:5165
5165            gcc_assert (fieldpos == local->total_bytes);
(gdb) p fieldpos
$1 = 16
(gdb) p local->total_bytes
$2 = 24

Andreas.
Marek Polacek Sept. 19, 2018, 2:05 p.m. UTC | #11
On Wed, Sep 19, 2018 at 03:25:02PM +0200, Andreas Schwab wrote:
> I'm getting this ICE on ia64:
> 
> libtool: compile:  /usr/local/gcc/gcc-20180919/Build/./gcc/xgcc -shared-libgcc -B/usr/local/gcc/gcc-20180919/Build/./gcc -nostdinc++ -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src/.libs -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/libsupc++/.libs -B/usr/ia64-suse-linux/bin/ -B/usr/ia64-suse-linux/lib/ -isystem /usr/ia64-suse-linux/include -isystem /usr/ia64-suse-linux/sys-include -fno-checking -I/usr/local/gcc/gcc-20180919/libstdc++-v3/../libgcc -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include/ia64-suse-linux -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include -I/usr/local/gcc/gcc-20180919/libstdc++-v3/libsupc++ -D_GLIBCXX_SHARED -fno-implicit-templates -Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi=2 -fdiagnostics-show-location=once -ffunction-sections -fdata-sections -frandom-seed=bad_typeid.lo -O2 -g -D_GNU_SOURCE -c ../../../../libstdc++-v3/libsupc++/bad_typeid.cc  -fPIC -DPIC -D_GLIBCXX_SHARED -o bad_typeid.o
> ../../../../libstdc++-v3/libsupc++/bad_typeid.cc:36:1: internal compiler error: in output_constructor_regular_field, at varasm.c:5165
> 36 | } // namespace std
>    | ^
> 0x400000000176abaf output_constructor_regular_field
>         ../../gcc/varasm.c:5165
> 0x400000000176d09f output_constructor
>         ../../gcc/varasm.c:5475
> 0x400000000176940f output_constant
>         ../../gcc/varasm.c:4967
> 0x400000000175414f assemble_variable_contents
>         ../../gcc/varasm.c:2143
> 0x400000000175586f assemble_variable(tree_node*, int, int, int)
>         ../../gcc/varasm.c:2319
> 0x40000000017a564f varpool_node::assemble_decl()
>         ../../gcc/varpool.c:586
> 0x40000000017a74cf symbol_table::output_variables()
>         ../../gcc/varpool.c:752
> 0x40000000007b806f symbol_table::compile()
>         ../../gcc/cgraphunit.c:2611
> 0x40000000007bd8ef symbol_table::compile()
>         ../../gcc/cgraphunit.c:2791
> 0x40000000007bd8ef symbol_table::finalize_compilation_unit()
>         ../../gcc/cgraphunit.c:2788
> 
> (gdb) up
> #1  0x400000000176abb0 in output_constructor_regular_field (
>     local=0x600ffffffffee920) at ../../gcc/varasm.c:5165
> 5165            gcc_assert (fieldpos == local->total_bytes);
> (gdb) p fieldpos
> $1 = 16
> (gdb) p local->total_bytes
> $2 = 24

That muset be because ia64 uses TARGET_VTABLE_USES_DESCRIPTORS.
Does this help?

2018-09-19  Marek Polacek  <polacek@redhat.com>

	* class.c (build_vtbl_initializer): Don't mess with *inits for
	TARGET_VTABLE_USES_DESCRIPTORS.

diff --git gcc/cp/class.c gcc/cp/class.c
index 9ca46441871..0239f6ae045 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo,
 	  int i;
 	  if (init == size_zero_node)
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
-	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
+	      CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
 	  else
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
 	      {
@@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo,
 				     fn, build_int_cst (NULL_TREE, i));
 		TREE_CONSTANT (fdesc) = 1;
 
-		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
+		CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
 	      }
 	}
       else
Andreas Schwab Sept. 19, 2018, 3:07 p.m. UTC | #12
On Sep 19 2018, Marek Polacek <polacek@redhat.com> wrote:

> 2018-09-19  Marek Polacek  <polacek@redhat.com>
>
> 	* class.c (build_vtbl_initializer): Don't mess with *inits for
> 	TARGET_VTABLE_USES_DESCRIPTORS.
>
> diff --git gcc/cp/class.c gcc/cp/class.c
> index 9ca46441871..0239f6ae045 100644
> --- gcc/cp/class.c
> +++ gcc/cp/class.c
> @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo,
>  	  int i;
>  	  if (init == size_zero_node)
>  	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> -	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> +	      CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
>  	  else
>  	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
>  	      {
> @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo,
>  				     fn, build_int_cst (NULL_TREE, i));
>  		TREE_CONSTANT (fdesc) = 1;
>  
> -		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
> +		CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
>  	      }
>  	}
>        else

Thanks, that works.

Andreas.
Marek Polacek Sept. 19, 2018, 3:10 p.m. UTC | #13
On Wed, Sep 19, 2018 at 05:07:34PM +0200, Andreas Schwab wrote:
> On Sep 19 2018, Marek Polacek <polacek@redhat.com> wrote:
> 
> > 2018-09-19  Marek Polacek  <polacek@redhat.com>
> >
> > 	* class.c (build_vtbl_initializer): Don't mess with *inits for
> > 	TARGET_VTABLE_USES_DESCRIPTORS.
> >
> > diff --git gcc/cp/class.c gcc/cp/class.c
> > index 9ca46441871..0239f6ae045 100644
> > --- gcc/cp/class.c
> > +++ gcc/cp/class.c
> > @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo,
> >  	  int i;
> >  	  if (init == size_zero_node)
> >  	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> > -	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> > +	      CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
> >  	  else
> >  	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> >  	      {
> > @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo,
> >  				     fn, build_int_cst (NULL_TREE, i));
> >  		TREE_CONSTANT (fdesc) = 1;
> >  
> > -		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
> > +		CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
> >  	      }
> >  	}
> >        else
> 
> Thanks, that works.

Great.  Jason, can I commit it then?

Marek
Jason Merrill Sept. 19, 2018, 5:25 p.m. UTC | #14
On Wed, Sep 19, 2018 at 11:10 AM, Marek Polacek <polacek@redhat.com> wrote:
> On Wed, Sep 19, 2018 at 05:07:34PM +0200, Andreas Schwab wrote:
>> On Sep 19 2018, Marek Polacek <polacek@redhat.com> wrote:
>>
>> > 2018-09-19  Marek Polacek  <polacek@redhat.com>
>> >
>> >     * class.c (build_vtbl_initializer): Don't mess with *inits for
>> >     TARGET_VTABLE_USES_DESCRIPTORS.
>> >
>> > diff --git gcc/cp/class.c gcc/cp/class.c
>> > index 9ca46441871..0239f6ae045 100644
>> > --- gcc/cp/class.c
>> > +++ gcc/cp/class.c
>> > @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo,
>> >       int i;
>> >       if (init == size_zero_node)
>> >         for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
>> > -         CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
>> > +         CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
>> >       else
>> >         for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
>> >           {
>> > @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo,
>> >                                  fn, build_int_cst (NULL_TREE, i));
>> >             TREE_CONSTANT (fdesc) = 1;
>> >
>> > -           CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
>> > +           CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
>> >           }
>> >     }
>> >        else
>>
>> Thanks, that works.
>
> Great.  Jason, can I commit it then?

Andreas, do the new testcases pass?  That would surprise me, but OK if so.

Jason
Andreas Schwab Sept. 20, 2018, 7:12 a.m. UTC | #15
On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote:

> Andreas, do the new testcases pass?  That would surprise me, but OK if so.

No, they don't.

/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
compiler exited with status 1
FAIL: g++.dg/cpp2a/constexpr-virtual2.C   (test for excess errors)

Andreas.
Jakub Jelinek Sept. 20, 2018, 9:20 a.m. UTC | #16
On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote:
> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote:
> 
> > Andreas, do the new testcases pass?  That would surprise me, but OK if so.
> 
> No, they don't.
> 
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
> compiler exited with status 1
> FAIL: g++.dg/cpp2a/constexpr-virtual2.C   (test for excess errors)

I think the primary problem here is:
      /* When using function descriptors, the address of the
         vtable entry is treated as a function pointer.  */
      if (TARGET_VTABLE_USES_DESCRIPTORS)
        e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
                     cp_build_addr_expr (e2, complain));
in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we
read the vtable function pointer.  On ia64, the above optimizes the
INDIRECT_REF away, so what the cxx_eval_call_expression actually gets
after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function,
but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ).

So, perhaps in cxx_eval_call_expression we need:
       if (TREE_CODE (fun) == ADDR_EXPR)
 	fun = TREE_OPERAND (fun, 0);
+      else if (TARGET_VTABLE_USES_DESCRIPTORS
+	       && TREE_CODE (fun) == POINTER_PLUS_EXPR
+	       && ...)
where we verify that p+ first argument is ADDR_EXPR of a virtual table,
second arg is INTEGER_CST and just walk the DECL_INITIAL of that, finding
the FDESC_EXPR at the right offset (therefore, I believe you need following
rather than the patch you've posted, so that you can actually find it) and
finally pick the function from the FDESC_EXPR entry.
Makes me wonder what happens with indirect calls in constexpr evaluation,
e.g. if I do:
constexpr int bar () { return 42; }
constexpr int foo () { int (*fn) () = bar; return fn (); }
static_assert (foo () == 42);
but apparently this works.

--- gcc/cp/class.c.jj	2018-09-20 09:56:59.229751895 +0200
+++ gcc/cp/class.c	2018-09-20 10:12:17.447370890 +0200
@@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo,
       tree vcall_index;
       tree fn, fn_original;
       tree init = NULL_TREE;
-      tree idx = size_int (jx++);
 
       fn = BV_FN (v);
       fn_original = fn;
@@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo,
 	  int i;
 	  if (init == size_zero_node)
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
-	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
+	      CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
 	  else
 	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
 	      {
@@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo,
 				     fn, build_int_cst (NULL_TREE, i));
 		TREE_CONSTANT (fdesc) = 1;
 
-		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
+		CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc);
 	      }
 	}
       else
-	CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
+	CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
     }
 }
 


	Jakub
Jason Merrill Sept. 27, 2018, 5:15 a.m. UTC | #17
On Thu, Sep 20, 2018 at 5:20 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote:
>> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote:
>>
>> > Andreas, do the new testcases pass?  That would surprise me, but OK if so.
>>
>> No, they don't.
>>
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion
>> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
>> compiler exited with status 1
>> FAIL: g++.dg/cpp2a/constexpr-virtual2.C   (test for excess errors)
>
> I think the primary problem here is:
>       /* When using function descriptors, the address of the
>          vtable entry is treated as a function pointer.  */
>       if (TARGET_VTABLE_USES_DESCRIPTORS)
>         e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
>                      cp_build_addr_expr (e2, complain));
> in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we
> read the vtable function pointer.  On ia64, the above optimizes the
> INDIRECT_REF away, so what the cxx_eval_call_expression actually gets
> after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function,
> but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ).
>
> So, perhaps in cxx_eval_call_expression we need:
>        if (TREE_CODE (fun) == ADDR_EXPR)
>         fun = TREE_OPERAND (fun, 0);
> +      else if (TARGET_VTABLE_USES_DESCRIPTORS
> +              && TREE_CODE (fun) == POINTER_PLUS_EXPR
> +              && ...)
> where we verify that p+ first argument is ADDR_EXPR of a virtual table,
> second arg is INTEGER_CST and just walk the DECL_INITIAL of that, finding
> the FDESC_EXPR at the right offset (therefore, I believe you need following
> rather than the patch you've posted, so that you can actually find it) and
> finally pick the function from the FDESC_EXPR entry.
> Makes me wonder what happens with indirect calls in constexpr evaluation,
> e.g. if I do:
> constexpr int bar () { return 42; }
> constexpr int foo () { int (*fn) () = bar; return fn (); }
> static_assert (foo () == 42);
> but apparently this works.
>
> --- gcc/cp/class.c.jj   2018-09-20 09:56:59.229751895 +0200
> +++ gcc/cp/class.c      2018-09-20 10:12:17.447370890 +0200
> @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo,
>        tree vcall_index;
>        tree fn, fn_original;
>        tree init = NULL_TREE;
> -      tree idx = size_int (jx++);
>
>        fn = BV_FN (v);
>        fn_original = fn;
> @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo,
>           int i;
>           if (init == size_zero_node)
>             for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> -             CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> +             CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
>           else
>             for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
>               {
> @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo,
>                                      fn, build_int_cst (NULL_TREE, i));
>                 TREE_CONSTANT (fdesc) = 1;
>
> -               CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
> +               CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc);
>               }
>         }
>        else
> -       CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> +       CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
>      }
>  }

This patch is OK.  And your suggestion for cxx_eval_call_expression
sounds right, too.  Marek, will you follow up on that?

Jason
Marek Polacek Sept. 27, 2018, 11:08 p.m. UTC | #18
On Thu, Sep 27, 2018 at 01:15:46AM -0400, Jason Merrill wrote:
> On Thu, Sep 20, 2018 at 5:20 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> > On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote:
> >> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote:
> >>
> >> > Andreas, do the new testcases pass?  That would surprise me, but OK if so.
> >>
> >> No, they don't.
> >>
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion
> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
> >> compiler exited with status 1
> >> FAIL: g++.dg/cpp2a/constexpr-virtual2.C   (test for excess errors)
> >
> > I think the primary problem here is:
> >       /* When using function descriptors, the address of the
> >          vtable entry is treated as a function pointer.  */
> >       if (TARGET_VTABLE_USES_DESCRIPTORS)
> >         e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
> >                      cp_build_addr_expr (e2, complain));
> > in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we
> > read the vtable function pointer.  On ia64, the above optimizes the
> > INDIRECT_REF away, so what the cxx_eval_call_expression actually gets
> > after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function,
> > but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ).
> >
> > So, perhaps in cxx_eval_call_expression we need:
> >        if (TREE_CODE (fun) == ADDR_EXPR)
> >         fun = TREE_OPERAND (fun, 0);
> > +      else if (TARGET_VTABLE_USES_DESCRIPTORS
> > +              && TREE_CODE (fun) == POINTER_PLUS_EXPR
> > +              && ...)
> > where we verify that p+ first argument is ADDR_EXPR of a virtual table,
> > second arg is INTEGER_CST and just walk the DECL_INITIAL of that, finding
> > the FDESC_EXPR at the right offset (therefore, I believe you need following
> > rather than the patch you've posted, so that you can actually find it) and
> > finally pick the function from the FDESC_EXPR entry.
> > Makes me wonder what happens with indirect calls in constexpr evaluation,
> > e.g. if I do:
> > constexpr int bar () { return 42; }
> > constexpr int foo () { int (*fn) () = bar; return fn (); }
> > static_assert (foo () == 42);
> > but apparently this works.
> >
> > --- gcc/cp/class.c.jj   2018-09-20 09:56:59.229751895 +0200
> > +++ gcc/cp/class.c      2018-09-20 10:12:17.447370890 +0200
> > @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo,
> >        tree vcall_index;
> >        tree fn, fn_original;
> >        tree init = NULL_TREE;
> > -      tree idx = size_int (jx++);
> >
> >        fn = BV_FN (v);
> >        fn_original = fn;
> > @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo,
> >           int i;
> >           if (init == size_zero_node)
> >             for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> > -             CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> > +             CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
> >           else
> >             for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> >               {
> > @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo,
> >                                      fn, build_int_cst (NULL_TREE, i));
> >                 TREE_CONSTANT (fdesc) = 1;
> >
> > -               CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
> > +               CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc);
> >               }
> >         }
> >        else
> > -       CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> > +       CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
> >      }
> >  }
> 
> This patch is OK.  And your suggestion for cxx_eval_call_expression
> sounds right, too.  Marek, will you follow up on that?

Ok, I will (provided I can get a box that has TARGET_VTABLE_USES_DESCRIPTORS, I
think ppc64 BE should be enough).

Marek
Jason Merrill Sept. 28, 2018, 12:46 a.m. UTC | #19
On Thu, Sep 27, 2018, 7:08 PM Marek Polacek <polacek@redhat.com> wrote:

> On Thu, Sep 27, 2018 at 01:15:46AM -0400, Jason Merrill wrote:
> > On Thu, Sep 20, 2018 at 5:20 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> > > On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote:
> > >> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote:
> > >>
> > >> > Andreas, do the new testcases pass?  That would surprise me, but OK
> if so.
> > >>
> > >> No, they don't.
> > >>
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26:
> error: non-constant condition for static assertion
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23:
> error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr'
> function
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26:
> error: non-constant condition for static assertion
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23:
> error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr'
> function
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27:
> error: non-constant condition for static assertion
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24:
> error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr'
> function
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26:
> error: non-constant condition for static assertion
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23:
> error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr'
> function
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26:
> error: non-constant condition for static assertion
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23:
> error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr'
> function
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27:
> error: non-constant condition for static assertion
> > >>
> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24:
> error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr'
> function
> > >> compiler exited with status 1
> > >> FAIL: g++.dg/cpp2a/constexpr-virtual2.C   (test for excess errors)
> > >
> > > I think the primary problem here is:
> > >       /* When using function descriptors, the address of the
> > >          vtable entry is treated as a function pointer.  */
> > >       if (TARGET_VTABLE_USES_DESCRIPTORS)
> > >         e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
> > >                      cp_build_addr_expr (e2, complain));
> > > in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we
> > > read the vtable function pointer.  On ia64, the above optimizes the
> > > INDIRECT_REF away, so what the cxx_eval_call_expression actually gets
> > > after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function,
> > > but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ).
> > >
> > > So, perhaps in cxx_eval_call_expression we need:
> > >        if (TREE_CODE (fun) == ADDR_EXPR)
> > >         fun = TREE_OPERAND (fun, 0);
> > > +      else if (TARGET_VTABLE_USES_DESCRIPTORS
> > > +              && TREE_CODE (fun) == POINTER_PLUS_EXPR
> > > +              && ...)
> > > where we verify that p+ first argument is ADDR_EXPR of a virtual table,
> > > second arg is INTEGER_CST and just walk the DECL_INITIAL of that,
> finding
> > > the FDESC_EXPR at the right offset (therefore, I believe you need
> following
> > > rather than the patch you've posted, so that you can actually find it)
> and
> > > finally pick the function from the FDESC_EXPR entry.
> > > Makes me wonder what happens with indirect calls in constexpr
> evaluation,
> > > e.g. if I do:
> > > constexpr int bar () { return 42; }
> > > constexpr int foo () { int (*fn) () = bar; return fn (); }
> > > static_assert (foo () == 42);
> > > but apparently this works.
> > >
> > > --- gcc/cp/class.c.jj   2018-09-20 09:56:59.229751895 +0200
> > > +++ gcc/cp/class.c      2018-09-20 10:12:17.447370890 +0200
> > > @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo,
> > >        tree vcall_index;
> > >        tree fn, fn_original;
> > >        tree init = NULL_TREE;
> > > -      tree idx = size_int (jx++);
> > >
> > >        fn = BV_FN (v);
> > >        fn_original = fn;
> > > @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo,
> > >           int i;
> > >           if (init == size_zero_node)
> > >             for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> > > -             CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> > > +             CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
> > >           else
> > >             for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> > >               {
> > > @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo,
> > >                                      fn, build_int_cst (NULL_TREE, i));
> > >                 TREE_CONSTANT (fdesc) = 1;
> > >
> > > -               CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
> > > +               CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++),
> fdesc);
> > >               }
> > >         }
> > >        else
> > > -       CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> > > +       CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
> > >      }
> > >  }
> >
> > This patch is OK.  And your suggestion for cxx_eval_call_expression
> > sounds right, too.  Marek, will you follow up on that?
>
> Ok, I will (provided I can get a box that has
> TARGET_VTABLE_USES_DESCRIPTORS, I
> think ppc64 BE should be enough).
>

Since this is a compile time issue, I would think a cross compiler would do
the trick?

>
Jakub Jelinek Sept. 28, 2018, 5:44 a.m. UTC | #20
On Thu, Sep 27, 2018 at 07:08:41PM -0400, Marek Polacek wrote:
> > This patch is OK.  And your suggestion for cxx_eval_call_expression
> > sounds right, too.  Marek, will you follow up on that?
> 
> Ok, I will (provided I can get a box that has TARGET_VTABLE_USES_DESCRIPTORS, I
> think ppc64 BE should be enough).

AFAIK only ia64 defines this macro to non-zero, so you need to cross-compile.
Given that this is constexpr stuff, all you are looking for is compile time
anyway ;)

	Jakub
Andreas Schwab Oct. 8, 2018, 1:36 p.m. UTC | #21
On Sep 20 2018, Jakub Jelinek <jakub@redhat.com> wrote:

> --- gcc/cp/class.c.jj	2018-09-20 09:56:59.229751895 +0200
> +++ gcc/cp/class.c	2018-09-20 10:12:17.447370890 +0200
> @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo,
>        tree vcall_index;
>        tree fn, fn_original;
>        tree init = NULL_TREE;
> -      tree idx = size_int (jx++);
>  
>        fn = BV_FN (v);
>        fn_original = fn;
> @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo,
>  	  int i;
>  	  if (init == size_zero_node)
>  	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
> -	      CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> +	      CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
>  	  else
>  	    for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
>  	      {
> @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo,
>  				     fn, build_int_cst (NULL_TREE, i));
>  		TREE_CONSTANT (fdesc) = 1;
>  
> -		CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc);
> +		CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc);
>  	      }
>  	}
>        else
> -	CONSTRUCTOR_APPEND_ELT (*inits, idx, init);
> +	CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
>      }
>  }

This still doesn't fix the tests.

Andreas.
Jakub Jelinek Oct. 10, 2018, 11:19 a.m. UTC | #22
On Mon, Oct 08, 2018 at 03:36:18PM +0200, Andreas Schwab wrote:
> This still doesn't fix the tests.

Sure, one needs to add the constexpr virtual calls for ia64 as I said in the
mail.

	Jakub
diff mbox series

Patch

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..8ebd86b611c 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,28 @@  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)
+	  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 +5910,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 +5918,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();
    ~~~~~~~~~ ^~~~~~~