c++: wrong error initializing empty class [PR115900]
In r14-409, we started handling empty bases first in cxx_fold_indirect_ref_1 so that we don't need to recurse and waste time. This caused a bogus "modifying a const object" error. I'm appending my analysis from the PR, but basically, cxx_fold_indirect_ref now returns a different object than before, and we mark the wrong thing as const, but since we're initializing an empty object, we should avoid setting the object constness. ~~ Pre-r14-409: we're evaluating the call to C::C(), which is in the body of B::B(), which is the body of D::D(&d): C::C ((struct C *) this, NON_LVALUE_EXPR <0>) It's a ctor so we get here: 3118 /* Remember the object we are constructing or destructing. */ 3119 tree new_obj = NULL_TREE; 3120 if (DECL_CONSTRUCTOR_P (fun) || DECL_DESTRUCTOR_P (fun)) 3121 { 3122 /* In a cdtor, it should be the first `this' argument. 3123 At this point it has already been evaluated in the call 3124 to cxx_bind_parameters_in_call. */ 3125 new_obj = TREE_VEC_ELT (new_call.bindings, 0); new_obj=(struct C *) &d.D.2656 3126 new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj); new_obj=d.D.2656.D.2597 We proceed to evaluate the call, then we get here: 3317 /* At this point, the object's constructor will have run, so 3318 the object is no longer under construction, and its possible 3319 'const' semantics now apply. Make a note of this fact by 3320 marking the CONSTRUCTOR TREE_READONLY. */ 3321 if (new_obj && DECL_CONSTRUCTOR_P (fun)) 3322 cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true, 3323 non_constant_p, overflow_p); new_obj is still d.D.2656.D.2597, its type is "C", cxx_set_object_constness doesn't set anything as const. This is fine. After r14-409: on line 3125, new_obj is (struct C *) &d.D.2656 as before, but we go to cxx_fold_indirect_ref_1: 5739 if (is_empty_class (type) 5740 && CLASS_TYPE_P (optype) 5741 && lookup_base (optype, type, ba_any, NULL, tf_none, off)) 5742 { 5743 if (empty_base) 5744 *empty_base = true; 5745 return op; type is C, which is an empty class; optype is "const D", and C is a base of D. So we return the VAR_DECL 'd'. Then we get to cxx_set_object_constness with object=d, which is const, so we mark the constructor READONLY. Then we're evaluating A::A() which has ((A*)this)->data = 0; we evaluate the LHS to d.D.2656.a, for which the initializer is {.D.2656={.a={.data=}}} which is TREE_READONLY and 'd' is const, so we think we're modifying a const object and fail the constexpr evaluation. PR c++/115900 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_call_expression): Set new_obj to NULL_TREE if cxx_fold_indirect_ref set empty_base to true. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-init23.C: New test. (cherry picked from commit d890b04197fb0ddba4fbfb32f88e266fa27e02f3)
This commit is contained in:
parent
a7f1b00ed6
commit
da7f0be91e
2 changed files with 32 additions and 4 deletions
|
@ -3123,9 +3123,15 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
|||
At this point it has already been evaluated in the call
|
||||
to cxx_bind_parameters_in_call. */
|
||||
new_obj = TREE_VEC_ELT (new_call.bindings, 0);
|
||||
new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj);
|
||||
|
||||
if (ctx->call && ctx->call->fundef
|
||||
bool empty_base = false;
|
||||
new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj,
|
||||
&empty_base);
|
||||
/* If we're initializing an empty class, don't set constness, because
|
||||
cxx_fold_indirect_ref will return the wrong object to set constness
|
||||
of. */
|
||||
if (empty_base)
|
||||
new_obj = NULL_TREE;
|
||||
else if (ctx->call && ctx->call->fundef
|
||||
&& DECL_CONSTRUCTOR_P (ctx->call->fundef->decl))
|
||||
{
|
||||
tree cur_obj = TREE_VEC_ELT (ctx->call->bindings, 0);
|
||||
|
|
22
gcc/testsuite/g++.dg/cpp2a/constexpr-init23.C
Normal file
22
gcc/testsuite/g++.dg/cpp2a/constexpr-init23.C
Normal file
|
@ -0,0 +1,22 @@
|
|||
// PR c++/115900
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
struct A {
|
||||
char m;
|
||||
constexpr A() { m = 0; }
|
||||
};
|
||||
|
||||
struct C {
|
||||
constexpr C(){ };
|
||||
};
|
||||
|
||||
struct B : C {
|
||||
A a;
|
||||
constexpr B() {}
|
||||
};
|
||||
|
||||
struct D : B { };
|
||||
|
||||
static constexpr A a;
|
||||
static constexpr B b;
|
||||
static constexpr D d;
|
Loading…
Add table
Reference in a new issue