Hi!

The following patch attempts to implement 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.

There are two places where I'm not sure what to do:

1) one can be seen on the constexpr-ice21.C testcase:
struct NoMut1 { int a, b; };
struct NoMut3 : virtual NoMut1 { constexpr NoMut3(int a, int b) : NoMut1{a, b} 
{} };
void mutable_subobjects() {
  constexpr NoMut3 nm3 = {1, 2};
  struct A {
    void f() {
      static_assert(nm3.a == 1, ""); // ERROR here: "local variable"
    }
  };
}
The two other errors on the testcase are expectedly gone with C++26,
but the last one remains.  The problem is that when parsing nm3.a
inside of mutable_subobjects()::A::f()
build_class_member_access_expr calls build_base_path which calls
cp_build_addr_expr and that makes nm3 odr-used.  I must say I have
no idea whether nm3 ought to be odr-used or not just because of nm3.a
use and if not, how that should be changed.  Plus whether the fact
if nm3.a is odr-use or not is somehow affected by the (so far unimplemented)
part of P2686R4 paper.

2) another one can be seen on the constexpr-dynamic10.C testcase
struct C { virtual void a(); };
struct B { virtual void b(); };
struct A : virtual B, C { virtual void c(); };
constexpr A a;
constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);   // ERROR here 
"reference 'dynamic_cast' failed"
I think the error is incorrect here, because
struct C { virtual void a(); };
struct B { virtual void b(); };
struct A : virtual B, C { virtual void c(); };
A a;
bool b1 = (dynamic_cast<C&>((B&)a), false);
int
main ()
{
  C &c = dynamic_cast<C&>((B&)a);
  C &c2 = dynamic_cast<C&>(a);
}
works at runtime.  In the patch I've adjusted the function
comment of cxx_eval_dynamic_cast_fn because with virtual bases
I believe hint -1 might be possible, though I'm afraid I don't
know enough about dynamic_cast and cxx_eval_dynamic_cast_fn
to figure out what needs to change there.  It is hint -2 that
fails, not hint -1.

In any case, this has been successfully bootstrapped/regtested on
x86_64-linux and i686-linux.

2025-06-24  Jakub Jelinek  <ja...@redhat.com>

        PR c++/120777
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.
        (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.
        * cp-tree.h (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.
gcc/testsuite/
        * g++.dg/cpp26/constexpr-virt-inherit1.C: New test.
        * g++.dg/cpp26/constexpr-virt-inherit2.C: New test.
        * g++.dg/cpp26/feat-cxx26.C: Add __cpp_constexpr_virtual_inheritance
        tersts.
        * g++.dg/cpp2a/constexpr-dtor16.C: Don't expect errors for C++26.
        * g++.dg/cpp2a/constexpr-dynamic10.C: Don't expect 2 errors for
        C++26, temporarily accept one invalid one.
        * g++.dg/cpp0x/constexpr-ice21.C: Don't expect 2 errors for C++26.
        * g++.dg/cpp0x/constexpr-ice4.C: Don't expect any errors for C++26.

--- gcc/c-family/c-cppbuiltin.cc.jj     2025-06-23 15:57:14.746215440 +0200
+++ gcc/c-family/c-cppbuiltin.cc        2025-06-23 18:35:02.776544246 +0200
@@ -1094,6 +1094,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_variadic_friend=202403L");
          cpp_define (pfile, "__cpp_pack_indexing=202311L");
          cpp_define (pfile, "__cpp_pp_embed=202502L");
+         cpp_define (pfile, "__cpp_constexpr_virtual_inheritance=202506L");
        }
       if (flag_concepts && cxx_dialect > cxx14)
        cpp_define (pfile, "__cpp_concepts=202002L");
--- gcc/cp/cp-tree.h.jj 2025-06-17 13:19:02.979959124 +0200
+++ gcc/cp/cp-tree.h    2025-06-23 21:17:58.569652869 +0200
@@ -8912,7 +8912,7 @@ extern tree fold_non_dependent_init               (tr
                                                 bool = false, tree = 
NULL_TREE);
 extern tree fold_simple                                (tree);
 extern tree fold_to_constant                   (tree);
-extern bool reduced_constant_expression_p       (tree);
+extern bool reduced_constant_expression_p       (tree, tree = NULL_TREE);
 extern bool is_instantiation_of_constexpr       (tree);
 extern bool var_in_constexpr_fn                 (tree);
 extern bool var_in_maybe_constexpr_fn           (tree);
--- gcc/cp/method.cc.jj 2025-06-03 07:49:37.861644267 +0200
+++ gcc/cp/method.cc    2025-06-24 10:54:41.306557859 +0200
@@ -3024,7 +3024,7 @@ synthesized_method_walk (tree ctype, spe
     /* Vbase cdtors are not relevant.  */;
   else
     {
-      if (constexpr_p)
+      if (constexpr_p && cxx_dialect < cxx26)
        *constexpr_p = false;
 
       FOR_EACH_VEC_ELT (*vbases, i, base_binfo)
--- gcc/cp/constexpr.cc.jj      2025-06-23 15:57:14.748215414 +0200
+++ gcc/cp/constexpr.cc 2025-06-24 12:28:49.561105734 +0200
@@ -303,17 +303,19 @@ is_valid_constexpr_fn (tree fun, bool co
            }
        }
     }
-  else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)))
+  else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)) && cxx_dialect < cxx26)
     {
       ret = false;
       if (complain)
        {
          if (DECL_CONSTRUCTOR_P (fun))
            error ("%<constexpr%> constructor in %q#T that has "
-                  "virtual base classes", DECL_CONTEXT (fun));
+                  "virtual base classes only available with "
+                  "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun));
          else
            error ("%<constexpr%> destructor in %q#T that has "
-                  "virtual base classes", DECL_CONTEXT (fun));
+                  "virtual base classes only available with "
+                  "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun));
        }
     }
 
@@ -1879,20 +1881,28 @@ addr_of_non_const_var (tree *tp, int *wa
 
 static tree
 cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
-                            bool *non_constant_p, bool *overflow_p,
-                            bool *non_constant_args)
+                            tree orig_fun, bool *non_constant_p,
+                            bool *overflow_p, bool *non_constant_args)
 {
-  const int nargs = call_expr_nargs (t);
+  int nargs = call_expr_nargs (t);
   tree parms = DECL_ARGUMENTS (fun);
-  int i;
+  int i, j = 0;
+  if (DECL_HAS_IN_CHARGE_PARM_P (fun) && fun != orig_fun)
+    ++nargs;
+  if (DECL_HAS_VTT_PARM_P (fun)
+      && fun != orig_fun
+      && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+         || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)))
+    ++nargs;
   /* We don't record ellipsis args below.  */
   int nparms = list_length (parms);
   int nbinds = nargs < nparms ? nargs : nparms;
   tree binds = make_tree_vec (nbinds);
 
   /* The call is not a constant expression if it involves the cdtor for a type
-     with virtual bases.  */
-  if (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun))
+     with virtual bases before C++26.  */
+  if (cxx_dialect < cxx26
+      && (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun)))
     {
       if (!ctx->quiet)
        {
@@ -1910,7 +1920,30 @@ cxx_bind_parameters_in_call (const const
       tree type = parms ? TREE_TYPE (parms) : void_type_node;
       if (parms && DECL_BY_REFERENCE (parms))
        type = TREE_TYPE (type);
-      x = get_nth_callarg (t, i);
+      if (i == 1
+         && j == 0
+         && DECL_HAS_IN_CHARGE_PARM_P (fun)
+         && orig_fun != fun)
+       {
+         if (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+             || DECL_COMPLETE_DESTRUCTOR_P (orig_fun))
+           x = boolean_true_node;
+         else
+           x = boolean_false_node;
+         j = -1;
+       }
+      else if (i == 2
+              && j == -1
+              && DECL_HAS_VTT_PARM_P (fun)
+              && orig_fun != fun
+              && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+                  || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)))
+       {
+         x = build_zero_cst (type);
+         j = -2;
+       }
+      else
+       x = get_nth_callarg (t, i + j);
       /* For member function, the first argument is a pointer to the implied
          object.  For a constructor, it might still be a dummy object, in
         which case we get the real argument from ctx.  */
@@ -2529,10 +2562,7 @@ get_component_with_type (tree path, tree
        dst_ptr + src2dst == src_ptr
    -1: unspecified relationship
    -2: src_type is not a public base of dst_type
-   -3: src_type is a multiple public non-virtual base of dst_type
-
-  Since literal types can't have virtual bases, we only expect hint >=0,
-  -2, or -3.  */
+   -3: src_type is a multiple public non-virtual base of dst_type  */
 
 static tree
 cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
@@ -2916,6 +2946,7 @@ cxx_eval_call_expression (const constexp
       *non_constant_p = true;
       return t;
     }
+  tree orig_fun = fun;
   if (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun))
     fun = DECL_CLONED_FUNCTION (fun);
 
@@ -3110,7 +3141,7 @@ cxx_eval_call_expression (const constexp
   bool non_constant_args = false;
   constexpr_call new_call;
   new_call.bindings
-    = cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p,
+    = cxx_bind_parameters_in_call (ctx, t, fun, orig_fun, non_constant_p,
                                   overflow_p, &non_constant_args);
 
   /* We build up the bindings list before we know whether we already have this
@@ -3514,11 +3545,12 @@ cxx_eval_call_expression (const constexp
 
 /* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
    initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
-   cleared.
+   cleared.  If called recursively on a FIELD_DECL's CONSTRUCTOR, SZ
+   is DECL_SIZE of the FIELD_DECL, otherwise NULL.
    FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
-reduced_constant_expression_p (tree t)
+reduced_constant_expression_p (tree t, tree sz /* = NULL_TREE */)
 {
   if (t == NULL_TREE)
     return false;
@@ -3586,7 +3618,12 @@ reduced_constant_expression_p (tree t)
        {
          /* If VAL is null, we're in the middle of initializing this
             element.  */
-         if (!reduced_constant_expression_p (e.value))
+         if (!reduced_constant_expression_p (e.value,
+                                             (e.index
+                                              && (TREE_CODE (e.index)
+                                                  == FIELD_DECL))
+                                             ? DECL_SIZE (e.index)
+                                             : NULL_TREE))
            return false;
          /* We want to remove initializers for empty fields in a struct to
             avoid confusing output_constructor.  */
@@ -3606,7 +3643,16 @@ reduced_constant_expression_p (tree t)
       /* There could be a non-empty field at the end.  */
       for (; field; field = next_subobject_field (DECL_CHAIN (field)))
        if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
-         return false;
+         {
+           /* Ignore FIELD_DECLs with bit positions beyond DECL_SIZE of
+              the parent FIELD_DECL (if any) for classes with virtual
+              bases.  */
+           if (cxx_dialect >= cxx26
+               && sz
+               && tree_int_cst_le (sz, bit_position (field)))
+             break;
+           return false;
+         }
 ok:
       if (CONSTRUCTOR_NO_CLEARING (t))
        /* All the fields are initialized.  */
@@ -5868,6 +5914,20 @@ cxx_fold_indirect_ref_1 (const constexpr
   unsigned HOST_WIDE_INT const_nunits;
   if (off == 0 && similar_type_p (optype, type))
     return op;
+  else if (cxx_dialect >= cxx26
+          && VAR_P (op)
+          && DECL_VTABLE_OR_VTT_P (op)
+          && same_type_ignoring_top_level_qualifiers_p (type,
+                                                        ptrdiff_type_node)
+          && POINTER_TYPE_P (strip_array_types (optype)))
+    {
+      /* We often read some virtual table elements using ptrdiff_t rather
+        than pointer type.  */
+      if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc,
+                                             strip_array_types (optype),
+                                             op, off, empty_base))
+       return fold_convert (type, ret);
+    }
   else if (TREE_CODE (optype) == COMPLEX_TYPE
           && similar_type_p (type, TREE_TYPE (optype)))
     {
@@ -5961,8 +6021,13 @@ cxx_fold_indirect_ref_1 (const constexpr
            if (!tree_fits_uhwi_p (pos))
              continue;
            unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos);
-           unsigned HOST_WIDE_INT el_sz
-             = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+           unsigned HOST_WIDE_INT el_sz;
+           if (DECL_FIELD_IS_BASE (field)
+               && CLASS_TYPE_P (optype)
+               && CLASSTYPE_VBASECLASSES (optype))
+             el_sz = tree_to_uhwi (DECL_SIZE_UNIT (field));
+           else
+             el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
            if (upos <= off && off < upos + el_sz)
              {
                tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
@@ -6013,6 +6078,25 @@ cxx_fold_indirect_ref (const constexpr_c
      offset positive, so that cxx_fold_indirect_ref_1 can identify
      more folding opportunities.  */
   auto canonicalize_obj_off = [] (tree& obj, tree& off) {
+    if (cxx_dialect >= cxx26)
+      {
+       /* For C++26, we need to fold *(B *)(&x.D.1234 + 32) used
+          to access virtual base members.  */
+       tree nobj = obj;
+       while (TREE_CODE (nobj) == COMPONENT_REF
+              && DECL_FIELD_IS_BASE (TREE_OPERAND (nobj, 1)))
+         nobj = TREE_OPERAND (nobj, 0);
+       if (nobj != obj
+           && CLASS_TYPE_P (TREE_TYPE (nobj))
+           && CLASSTYPE_VBASECLASSES (TREE_TYPE (nobj)))
+         while (obj != nobj)
+           {
+             tree field = TREE_OPERAND (obj, 1);
+             tree pos = byte_position (field);
+             off = int_const_binop (PLUS_EXPR, off, pos);
+             obj = TREE_OPERAND (obj, 0);
+           }
+      }
     while (TREE_CODE (obj) == COMPONENT_REF
           /* We need to preserve union member accesses so that we can
              later properly diagnose accessing the wrong member.  */
--- gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C.jj     2025-06-23 
20:13:23.617647761 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C        2025-06-23 
21:47:35.068198053 +0200
@@ -0,0 +1,189 @@
+// C++26 P3533R2 - constexpr virtual inheritance
+// { dg-do compile { target c++26 } }
+
+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 (), e (58) {}
+  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;
+  C g (48);
+  c = static_cast <C *> (&g);
+  b = static_cast <B *> (&g);
+  a = static_cast <A *> (&g);
+  g.a++;
+  g.b++;
+  g.c++;
+  if (g.foo () != 45 + 44)
+    return false;
+  if (g.bar () != 45 + 44 + 49)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  c->c += 4;
+  if (c->foo () != 47 + 47)
+    return false;
+  if (g.a != 47 || g.b != 47 || g.c != 53)
+    return false;
+  D h (49);
+  d = static_cast <D *> (&h);
+  b = static_cast <B *> (&h);
+  a = static_cast <A *> (&h);
+  h.a++;
+  h.b++;
+  h.d++;
+  if (h.foo () != 45 + 44)
+    return false;
+  if (h.baz () != 45 + 44 + 50)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  d->d += 4;
+  if (d->foo () != 47 + 47)
+    return false;
+  if (h.a != 47 || h.b != 47 || h.d != 54)
+    return false;
+  return true;
+}
+
+constexpr bool
+corge ()
+{
+  E *f = new E (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;
+  C *g = new C (48);
+  c = static_cast <C *> (g);
+  b = static_cast <B *> (g);
+  a = static_cast <A *> (g);
+  g->a++;
+  g->b++;
+  g->c++;
+  if (g->foo () != 45 + 44)
+    return false;
+  if (g->bar () != 45 + 44 + 49)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  c->c += 4;
+  if (c->foo () != 47 + 47)
+    return false;
+  if (g->a != 47 || g->b != 47 || g->c != 53)
+    return false;
+  D *h = new D (49);
+  d = static_cast <D *> (h);
+  b = static_cast <B *> (h);
+  a = static_cast <A *> (h);
+  h->a++;
+  h->b++;
+  h->d++;
+  if (h->foo () != 45 + 44)
+    return false;
+  if (h->baz () != 45 + 44 + 50)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  d->d += 4;
+  if (d->foo () != 47 + 47)
+    return false;
+  if (h->a != 47 || h->b != 47 || h->d != 54)
+    return false;
+  delete h;
+  delete g;
+  delete f;
+  return true;
+}
+
+static_assert (qux ());
+static_assert (corge ());
+constexpr E a;
+constexpr E b (45, 46, 47, 48, 49);
+constexpr C c;
+constexpr C d (50);
--- gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C.jj     2025-06-24 
10:56:07.642430579 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C        2025-06-24 
10:58:44.014388836 +0200
@@ -0,0 +1,18 @@
+// C++26 P3533R2 - constexpr virtual inheritance
+// { dg-do compile { target c++26 } }
+
+struct A { int a; };
+struct B { int b; };
+struct C : virtual public A, B { int c; };
+
+constexpr C
+foo ()
+{
+  C c;
+  c.a = 1;
+  c.b = 2;
+  c.c = 3;
+  return c;
+}
+
+static_assert (foo ().a == 1 && foo ().b == 2 && foo ().c == 3);
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj  2025-06-17 13:18:52.979091237 
+0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C     2025-06-23 20:10:45.486631704 
+0200
@@ -634,3 +634,9 @@
 #elif __cpp_pp_embed != 202502
 #  error "__cpp_pp_embed != 202502"
 #endif
+
+#ifndef __cpp_constexpr_virtual_inheritance
+# error "__cpp_constexpr_virtual_inheritance"
+#elif __cpp_constexpr_virtual_inheritance != 202506
+#  error "__cpp_constexpr_virtual_inheritance != 202506"
+#endif
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C.jj    2025-04-18 
11:23:53.870174278 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C       2025-06-24 
10:16:01.394915120 +0200
@@ -3,5 +3,5 @@
 
 struct A { virtual ~A (); };
 struct B : virtual A { constexpr ~B () {} };
-// { dg-error "'constexpr' destructor in 'struct B' that has virtual base 
classes" "" { target c++20 } .-1 }
+// { dg-error "'constexpr' destructor in 'struct B' that has virtual base 
classes" "" { target { c++20 && c++23_down } } .-1 }
 // { dg-error "'constexpr' destructors only available with" "" { target 
c++17_down } .-2 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C.jj 2025-04-18 
11:23:53.870174278 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C    2025-06-24 
12:32:35.299213775 +0200
@@ -5,8 +5,8 @@
 
 struct C { virtual void a(); };
 struct B { virtual void b(); };
-struct A : virtual B, C { virtual void c(); }; // { dg-error "virtual base 
classes" }
+struct A : virtual B, C { virtual void c(); }; // { dg-error "virtual base 
classes" "" { target c++23_down } }
 
-constexpr A a; // { dg-error "call" }
+constexpr A a; // { dg-error "call" "" { target c++23_down } }
 
-constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
+constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);  // { dg-error 
"reference 'dynamic_cast' failed" "" { target c++26 } }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C.jj     2025-04-17 
10:56:43.362325247 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C        2025-06-24 
10:16:01.438914543 +0200
@@ -5,10 +5,10 @@ struct NoMut1 { int a, b; };
 struct NoMut3 : virtual NoMut1 {
   constexpr NoMut3(int a, int b)
     : NoMut1{a, b}
-  {} // { dg-error "virtual base" }
+  {} // { dg-error "virtual base" "" { target c++23_down } }
 };
 void mutable_subobjects() {
-  constexpr NoMut3 nm3 = {1, 2}; // { dg-error "call to non" }
+  constexpr NoMut3 nm3 = {1, 2}; // { dg-error "call to non" "" { target 
c++23_down } }
   struct A {
     void f() {
       static_assert(nm3.a == 1, ""); // { dg-error "local variable" }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C.jj      2020-01-12 
11:54:37.060403647 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C 2025-06-24 10:16:01.414914857 
+0200
@@ -5,5 +5,5 @@ struct A {};
 
 struct B : virtual A
 {
-  constexpr B() { } // { dg-error "has virtual base classes" }
+  constexpr B() { } // { dg-error "has virtual base classes" "" { target 
c++23_down } }
 };

        Jakub

Reply via email to