Bug 120777 - [C++26] P3533R2 - constexpr virtual inheritance
Summary: [C++26] P3533R2 - constexpr virtual inheritance
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 16.0
: P3 enhancement
Target Milestone: 16.0
Assignee: Jakub Jelinek
URL:
Keywords: c++26
Depends on:
Blocks: constexpr c++26-core
  Show dependency treegraph
 
Reported: 2025-06-23 09:08 UTC by Jakub Jelinek
Modified: 2026-01-20 09:44 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2025-06-23 00:00:00


Attachments
gcc16-pr120777-wip.patch (2.29 KB, patch)
2025-06-23 13:56 UTC, Jakub Jelinek
Details | Diff
gcc16-pr120777-wip.patch (3.55 KB, patch)
2025-06-23 18:21 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jakub Jelinek 2025-06-23 09:08:37 UTC
See <https://wg21.link/P3533R2>.
Comment 1 Andrew Pinski 2025-06-23 09:27:55 UTC
.
Comment 2 Jakub Jelinek 2025-06-23 13:56:47 UTC
Created attachment 61688 [details]
gcc16-pr120777-wip.patch

Started playing with this and made some progess, but
struct A {
  int a;
  constexpr virtual int foo () { return a; };
  constexpr A () : a (42) {}
  constexpr A (int x) : a (x) {}
  constexpr virtual ~A () { if (a < 42 || a > 62) asm (""); }
};
struct B : public A {
  int b;
  constexpr virtual int foo () { return a + b; }
  constexpr B () : A (43), b (42) {}
  constexpr B (int x, int y) : A (x), b (y) {}
  constexpr virtual ~B () { if (b < 42 || b > 62) asm (""); }
};
struct C : virtual public B {
  int c;
  constexpr C () : B (44, 43), c (45) {}
  constexpr C (int x) : B (44, 43), c (x) {}
  constexpr virtual int bar () { return a + b + c; }
  constexpr virtual ~C () { if (c < 42 || c > 62) asm (""); }
};
struct D : virtual public B {
  int d;
  constexpr D () : B (44, 43), d (45) {}
  constexpr D (int x) : B (44, 43), d (x) {}
  constexpr virtual int baz () { return a + b + d; }
  constexpr virtual ~D () { if (d < 42 || d > 62) asm (""); }
};
struct E : public C, D {
  int e;
  constexpr E () : B (), C (), D () {}
  constexpr E (int x, int y, int z, int w, int v) : B (x, y), C (z), D (w), e (v) {}
  constexpr virtual ~E () { if (e < 42 || e > 62) asm (""); }
};

constexpr bool
qux ()
{
  E f (45, 46, 47, 48, 49);
  f.a++;
  f.b++;
  f.c++;
  f.d++;
  f.e++;
  C *c = static_cast <C *> (&f);
  D *d = static_cast <D *> (&f);
  B *b = static_cast <B *> (&f);
  A *a = static_cast <A *> (&f);
  if (f.foo () != 46 + 47)
    return false;
  if (f.bar () != 46 + 47 + 48)
    return false;
  if (f.baz () != 46 + 47 + 49)
    return false;
  a->a += 2;
  b->b += 3;
  c->c += 4;
  c->a += 5;
  d->d += 6;
  d->a += 7;
  if (c->foo () != 60 + 50)
    return false;
  c->b -= 3;
  if (d->foo () != 60 + 47)
    return false;
  if (f.a != 60 || f.b != 47 || f.c != 52 || f.d != 55 || f.e != 50)
    return false;
  return true;
}

static_assert (qux ());

still doesn't work yet.
Comment 3 Jakub Jelinek 2025-06-23 18:21:01 UTC
Created attachment 61695 [details]
gcc16-pr120777-wip.patch

Some further progress, the testcase now passes.
Except uncommenting the last two lines still results in errors.
I think there are 2 problems.
One is that at the end of the complete constructor for E we get something like
{.D.3067={._vptr.C=&_ZTV1E + 24, .c=47}, .D.3068={._vptr.D=&_ZTV1E + 72, .d=48, .D.2967={.D.2757={._vptr.A=&_ZTV1E + 128}}}, .e=49, .D.3073={.D.2757={._vptr.A=&_ZTV1B + 16, .a=45}, .b=46}}
The problem is the D.2967= initialization there, I bet we need it during the construction, but maybe at the end of the complete ctor for a class with virtual bases we should remove all initializers in the CONSTRUCTOR which are beyond DECL_SIZE of the containing FIELD_DECL for bases.

And the other are the
abcu.C:183:13: error: ‘E{C{((& E::_ZTV1E) + 24), 45}, D{((& E::_ZTV1E) + 72), 45}, B{A{((& E::_ZTV1E) + 128), 43}, 42}}’ is not a constant expression
  183 | constexpr E a;
      |             ^
abcu.C:183:13: error: ‘((E*)(& a))->E::E()’ is not a constant expression because it refers to an incompletely initialized variable
abcu.C:184:34: error: ‘E{C{((& E::_ZTV1E) + 24), 47}, D{((& E::_ZTV1E) + 72), 48}, 49, B{A{((& E::_ZTV1E) + 128), 45}, 46}}’ is not a constant expression
  184 | constexpr E b (45, 46, 47, 48, 49);
      |                                  ^
abcu.C:184:34: error: ‘((E*)(& b))->E::E(45, 46, 47, 48, 49)’ is not a constant expression because it refers to an incompletely initialized variable
errors, perhaps on the other side we want to verify the bits beyond DECL_SIZE of FIELD_DECLs aren't initialized rather than flagging them as incompletely initialized.
Comment 4 GCC Commits 2025-06-27 21:42:01 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

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

commit r16-1751-gf86ebb00406dafed3b8ebeae6e9e9d613abdf2ec
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Fri Jun 27 23:39:30 2025 +0200

    c++: Implement C++26 P3533R2 - constexpr virtual inheritance [PR120777]
    
    The following patch implements the C++26
    P3533R2 - constexpr virtual inheritance
    paper.
    The changes include not rejecting it for C++26, tweaking the
    error wording to show that it is valid in C++26, adjusting
    synthesized_method_walk not to make synthetized cdtors non-constexpr
    just because of virtual base classes in C++26 and various tweaks in
    constexpr.cc so that it can deal with the expressions used for
    virtual base member accesses or cdtor calls which need __in_chrg
    and/or __vtt_parm arguments to be passed in some cases implicitly when
    they aren't passed explicitly.  And dynamic_cast constant evaluation
    tweaks so that it handles also expressions with types with virtual bases.
    
    2025-06-27  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/120777
    gcc/
            * gimple-fold.cc (gimple_get_virt_method_for_vtable): Revert
            2018-09-18 changes.
    gcc/c-family/
            * c-cppbuiltin.cc (c_cpp_builtins): Predefine
            __cpp_constexpr_virtual_inheritance=202506L for C++26.
    gcc/cp/
            * constexpr.cc: Implement C++26 P3533R2 - constexpr virtual
            inheritance.
            (is_valid_constexpr_fn): Don't reject constexpr cdtors in classes
            with virtual bases for C++26, adjust error wording.
            (cxx_bind_parameters_in_call): Add ORIG_FUN argument, add
            values for __in_chrg and __vtt_parm arguments when needed.
            (cxx_eval_dynamic_cast_fn): Adjust function comment, HINT -1
            should be possible.  For C++26 if obj is cast from POINTER_PLUS_EXPR,
            attempt to use cxx_fold_indirect_ref to simplify it and if successful,
            build ADDR_EXPR of that.
            (cxx_eval_call_expression): Add orig_fun variable, set it to
            fun before looking through clones, pass it to
            cxx_bind_parameters_in_call.
            (reduced_constant_expression_p): Add SZ argument, pass DECL_SIZE
            of FIELD_DECL e.index to recursive calls and don't return false
            if SZ is non-NULL and there are unfilled fields with bit position
            at or above SZ.
            (cxx_fold_indirect_ref_1): Handle reading of vtables using
            ptrdiff_t dynamic type instead of some pointer type.  Set el_sz
            to DECL_SIZE_UNIT value rather than TYPE_SIZE_UNIT of
            DECL_FIELD_IS_BASE fields in classes with virtual bases.
            (cxx_fold_indirect_ref): In canonicalize_obj_off lambda look
            through COMPONENT_REFs with DECL_FIELD_IS_BASE in classes with
            virtual bases and adjust off correspondingly.  Remove assertion that
            off is integer_zerop, pass tree_to_uhwi (off) instead of 0 to the
            cxx_fold_indirect_ref_1 call.
            * cp-tree.h (publicly_virtually_derived_p): Declare.
            (reduced_constant_expression_p): Add another tree argument defaulted
            to NULL_TREE.
            * method.cc (synthesized_method_walk): Don't clear *constexpr_p
            if there are virtual bases for C++26.
            * class.cc (build_base_path): Compute fixed_type_p and
            virtual_access before checks for build_simple_base_path instead of
            after that and conditional cp_build_addr_expr.  Use build_simple_path
            if !virtual_access even when v_binfo is non-NULL.
            (layout_virtual_bases): For build_base_field calls use
            access_public_node rather than access_private_node if
            publicly_virtually_derived_p.
            (build_vtbl_initializer): Revert 2018-09-18 and 2018-12-11 changes.
            (publicly_virtually_derived_p): New function.
    gcc/testsuite/
            * g++.dg/cpp26/constexpr-virt-inherit1.C: New test.
            * g++.dg/cpp26/constexpr-virt-inherit2.C: New test.
            * g++.dg/cpp26/constexpr-virt-inherit3.C: New test.
            * g++.dg/cpp26/feat-cxx26.C: Add __cpp_constexpr_virtual_inheritance
            tersts.
            * g++.dg/cpp2a/constexpr-dtor3.C: Don't expect one error for C++26.
            * g++.dg/cpp2a/constexpr-dtor16.C: Don't expect errors for C++26.
            * g++.dg/cpp2a/constexpr-dynamic10.C: Likewise.
            * g++.dg/cpp0x/constexpr-ice21.C: Likewise.
            * g++.dg/cpp0x/constexpr-ice4.C: Likewise.
            * g++.dg/abi/mangle1.C: Guard the test on c++23_down.
            * g++.dg/abi/mangle81.C: New test.
            * g++.dg/ipa/ipa-icf-4.C (A::A): For
            __cpp_constexpr_virtual_inheritance >= 202506L add user provided
            non-constexpr constructor.
Comment 5 Jakub Jelinek 2025-06-27 22:06:42 UTC
Implemented for 16.1+.