Commit 6b45b400 by Jason Merrill

c++: Fix complex constexpr virtual cases [PR93310].

The code in constexpr for looking up the actual type of the object and then
getting the virtual function from there broke for both of these tests: for
16, it assumed incorrectly that the DECL_VINDEX would apply to the most
derived type's vtable; for 17, it failed to consider that during
construction the base subobject is treated as being of the base type.

Fixed by just doing constant evaluation of the expression that looks up the
function in the vtable.  This means that a virtual call will involve loading
the vptr, so we will reject some calls through non-constexpr variables that
we previously accepted, but this seems appropriate to me.  None of our
testcases were affected.

gcc/cp/ChangeLog:

	PR c++/93310
	* constexpr.c (cxx_eval_constant_expression) [OBJ_TYPE_REF]:
	Evaluate OBJ_TYPE_REF_EXPR.

gcc/testsuite/ChangeLog:

	PR c++/93310
	* g++.dg/cpp2a/constexpr-virtual16.C: New test.
	* g++.dg/cpp2a/constexpr-virtual17.C: New test.
	* g++.dg/cpp2a/constexpr-new12.C: Adjust diagnostic.
parent e244b0ac
...@@ -6307,47 +6307,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ...@@ -6307,47 +6307,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
break; break;
case OBJ_TYPE_REF: case OBJ_TYPE_REF:
{ /* Virtual function lookup. We don't need to do anything fancy. */
/* Virtual function call. Let the constexpr machinery figure out return cxx_eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t),
the dynamic type. */ lval, non_constant_p, overflow_p);
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);
STRIP_NOPS (obj);
/* We expect something in the form of &x.D.2103.D.2094; get x. */
if (TREE_CODE (obj) != ADDR_EXPR
|| !DECL_P (get_base_address (TREE_OPERAND (obj, 0))))
{
if (!ctx->quiet)
error_at (loc, "expression %qE is not a constant expression", t);
*non_constant_p = true;
return t;
}
obj = TREE_OPERAND (obj, 0);
while (TREE_CODE (obj) == COMPONENT_REF
&& DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)))
obj = TREE_OPERAND (obj, 0);
tree objtype = TREE_TYPE (obj);
if (VAR_P (obj)
&& DECL_NAME (obj) == heap_identifier
&& TREE_CODE (objtype) == ARRAY_TYPE)
objtype = TREE_TYPE (objtype);
if (!CLASS_TYPE_P (objtype))
{
if (!ctx->quiet)
error_at (loc, "expression %qE is not a constant expression", t);
*non_constant_p = true;
return t;
}
/* 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));
if (TARGET_VTABLE_USES_DESCRIPTORS)
token /= MAX (TARGET_VTABLE_USES_DESCRIPTORS, 1);
r = TREE_VALUE (chain_index (token, virtuals));
break;
}
case PLACEHOLDER_EXPR: case PLACEHOLDER_EXPR:
/* Use of the value or address of the current object. */ /* Use of the value or address of the current object. */
......
...@@ -24,4 +24,4 @@ foo () ...@@ -24,4 +24,4 @@ foo ()
return r; return r;
} }
constexpr auto a = foo (); // { dg-error "is not a constant expression" } constexpr auto a = foo (); // { dg-error "constant expression" }
// Test constexpr virtual in non-primary vtable.
// { dg-do compile { target c++20 } }
struct A
{
virtual constexpr int f() const { return 1; };
};
struct B
{
virtual constexpr int g() const { return 2; };
};
struct C: A, B
{
};
constexpr C c;
constexpr int g(const B& b) { return b.g(); }
static_assert (g(c) == 2);
// PR c++/93310
// { dg-do compile { target c++20 } }
struct A
{
virtual constexpr char f () const
{ return 'A'; }
};
struct B : A
{
char x;
constexpr B () : x (0)
{ x = ((A *)this)->f(); }
virtual constexpr char f () const
{ return 'B'; }
};
struct C : B
{
virtual constexpr char f () const
{ return 'C'; }
};
constexpr C c;
static_assert (c.x == 'B');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment