https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115900

--- Comment #8 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The trunk branch has been updated by Marek Polacek <mpola...@gcc.gnu.org>:

https://gcc.gnu.org/g:d890b04197fb0ddba4fbfb32f88e266fa27e02f3

commit r15-2108-gd890b04197fb0ddba4fbfb32f88e266fa27e02f3
Author: Marek Polacek <pola...@redhat.com>
Date:   Wed Jul 17 11:19:32 2024 -0400

    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.

Reply via email to