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.
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
- && DECL_CONSTRUCTOR_P (ctx->call->fundef->decl))
+ 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);
STRIP_NOPS (cur_obj);
--- /dev/null
+// 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;