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");
|| processing_template_decl
|| in_template_context);
+ fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
+
+ /* Do we need to look in the vtable for the real offset? */
+ virtual_access = (v_binfo && fixed_type_p <= 0);
+
/* For a non-pointer simple base reference, express it as a COMPONENT_REF
without taking its address (and so causing lambda capture, 91933). */
- if (code == PLUS_EXPR && !v_binfo && !want_pointer && !has_empty && !uneval)
+ if (code == PLUS_EXPR
+ && !want_pointer
+ && !has_empty
+ && !uneval
+ && !virtual_access)
return build_simple_base_path (expr, binfo);
if (!want_pointer)
expr = mark_rvalue_use (expr);
offset = BINFO_OFFSET (binfo);
- fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
target_type = code == PLUS_EXPR ? BINFO_TYPE (binfo) : BINFO_TYPE (d_binfo);
/* TARGET_TYPE has been extracted from BINFO, and, is therefore always
cv-unqualified. Extract the cv-qualifiers from EXPR so that the
(target_type, cp_type_quals (TREE_TYPE (TREE_TYPE (expr))));
ptr_target_type = build_pointer_type (target_type);
- /* Do we need to look in the vtable for the real offset? */
- virtual_access = (v_binfo && fixed_type_p <= 0);
-
/* Don't bother with the calculations inside sizeof; they'll ICE if the
source type is incomplete and the pointer value doesn't matter. In a
template (even in instantiate_non_dependent_expr), we don't have vtables
{
/* This virtual base is not a primary base of any class in the
hierarchy, so we have to add space for it. */
- next_field = build_base_field (rli, vbase,
- access_private_node,
- offsets, next_field);
+ tree access = access_private_node;
+ if (publicly_virtually_derived_p (BINFO_TYPE (vbase), t))
+ access = access_public_node;
+ next_field = build_base_field (rli, vbase, access, offsets,
+ next_field);
}
}
}
int i;
if (init == size_zero_node)
for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
- CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
+ CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
else
for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
{
fn, build_int_cst (NULL_TREE, i));
TREE_CONSTANT (fdesc) = 1;
- CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc);
+ CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
}
}
else
- CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
+ CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
}
}
return base && base != error_mark_node;
}
+/* TRUE iff TYPE is publicly & virtually derived from PARENT. */
+
+bool
+publicly_virtually_derived_p (tree parent, tree type)
+{
+ tree base = lookup_base (type, parent,
+ ba_ignore_scope | ba_check | ba_require_virtual,
+ NULL, tf_none);
+ return base && base != error_mark_node;
+}
+
/* CTX1 and CTX2 are declaration contexts. Return the innermost common
class between them, if any. */
}
}
}
- 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));
}
}
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)
{
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. */
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,
if (*non_constant_p)
return call;
+ /* For dynamic_cast from classes with virtual bases we can get something
+ like (virt_base *)(&d + 16) as OBJ. Try to convert that into
+ d.D.1234 using cxx_fold_indirect_ref. */
+ if (cxx_dialect >= cxx26 && CONVERT_EXPR_P (obj))
+ {
+ tree objo = obj;
+ STRIP_NOPS (objo);
+ if (TREE_CODE (objo) == POINTER_PLUS_EXPR)
+ {
+ objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)),
+ obj);
+ if (objo)
+ obj = build_fold_addr_expr (objo);
+ }
+ }
+
/* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
but when HINT is > 0, it can also be something like
&d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET. */
*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);
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
/* 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;
{
/* 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. */
/* 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. */
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)))
{
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),
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. */
{
tree off = integer_zero_node;
canonicalize_obj_off (op, off);
- gcc_assert (integer_zerop (off));
- return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base);
+ return cxx_fold_indirect_ref_1 (ctx, loc, type, op,
+ tree_to_uhwi (off), empty_base);
}
}
else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
extern void deduce_noexcept_on_destructor (tree);
extern bool uniquely_derived_from_p (tree, tree);
extern bool publicly_uniquely_derived_p (tree, tree);
+extern bool publicly_virtually_derived_p (tree, tree);
extern tree common_enclosing_class (tree, tree);
/* in cvt.cc */
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);
/* 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)
access_index = offset / BITS_PER_UNIT / elt_size;
gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0);
- /* The C++ FE can now produce indexed fields, and we check if the indexes
- match. */
+ /* This code makes an assumption that there are no
+ indexed fileds produced by C++ FE, so we can directly index the array. */
if (access_index < CONSTRUCTOR_NELTS (init))
{
fn = CONSTRUCTOR_ELT (init, access_index)->value;
- tree idx = CONSTRUCTOR_ELT (init, access_index)->index;
- gcc_checking_assert (!idx || tree_to_uhwi (idx) == access_index);
+ gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
STRIP_NOPS (fn);
}
else
// Test for mangling of simple testcase involving construction vtables.
-// { dg-do compile }
+// For C++26, the ctor is constant evaluated and so construction vtables
+// aren't needed.
+// { dg-do compile { target c++23_down } }
// { dg-options "-fno-inline -fabi-compat-version=0" }
struct A {
--- /dev/null
+// Test for mangling of simple testcase involving construction vtables.
+
+// { dg-do compile }
+// { dg-options "-fno-inline -fabi-compat-version=0" }
+
+struct A {
+ virtual void f () { }
+ A () {}
+};
+
+struct B: public virtual A { };
+struct C: public B { };
+
+C c;
+
+// { dg-final { scan-assembler "\n_?_ZN1A1fEv\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZN1AC2Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZN1BC2Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZN1CC1Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTC1C0_1B\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTI1A\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTI1B\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTI1C\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTS1A\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTS1B\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTS1C\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTT1C\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTV1A\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTV1C\[: \t\n\]" } }
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" }
+ static_assert(nm3.a == 1, ""); // { dg-error "local variable" "" { target c++23_down } }
}
};
}
struct B : virtual A
{
- constexpr B() { } // { dg-error "has virtual base classes" }
+ constexpr B() { } // { dg-error "has virtual base classes" "" { target c++23_down } }
};
--- /dev/null
+// 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);
--- /dev/null
+// 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);
--- /dev/null
+// C++26 P3533R2 - constexpr virtual inheritance
+// { dg-do compile { target c++26 } }
+
+#define M(N, P1, P2, P3, P4, P5, P6, N1, N2, N3) \
+struct S##N { \
+ int a, b; \
+ constexpr S##N () : a (0), b (0) {} \
+ constexpr virtual int bar (int) { return 0; } \
+}; \
+struct T##N : virtual P1 S##N { \
+ int c, d; \
+ constexpr T##N () : c (0), d (0) {} \
+}; \
+struct U##N : virtual P2 S##N, virtual P3 T##N { \
+ int e; \
+ constexpr U##N () : e (0) {} \
+}; \
+struct V##N : virtual P4 S##N, virtual P5 T##N, virtual P6 U##N { \
+ int f; \
+ constexpr V##N () : f (0) {} \
+ constexpr const S##N *foo () const { return (const S##N *)this; } \
+}; \
+constexpr V##N v##N; \
+static_assert (N1 !!dynamic_cast<const V##N *> (v##N.foo ())); \
+static_assert (N2 !!dynamic_cast<const T##N *> (v##N.foo ())); \
+static_assert (N3 !!dynamic_cast<const U##N *> (v##N.foo ()));
+
+M(0, public, public, public, public, public, public, , , )
+M(1, private, public, public, public, public, public, , , )
+M(2, public, private, public, public, public, public, , , )
+M(3, private, private, public, public, public, public, , , )
+M(4, public, public, private, public, public, public, , , )
+M(5, private, public, private, public, public, public, , , )
+M(6, public, private, private, public, public, public, , , )
+M(7, private, private, private, public, public, public, , , )
+M(8, public, public, public, private, public, public, , , )
+M(9, private, public, public, private, public, public, , , )
+M(10, public, private, public, private, public, public, , , )
+M(11, private, private, public, private, public, public, !, !, !)
+M(12, public, public, private, private, public, public, , , )
+M(13, private, public, private, private, public, public, , , )
+M(14, public, private, private, private, public, public, , , )
+M(15, private, private, private, private, public, public, !, !, !)
+M(16, public, public, public, public, private, public, , , )
+M(17, private, public, public, public, private, public, , , )
+M(18, public, private, public, public, private, public, , , )
+M(19, private, private, public, public, private, public, , , )
+M(20, public, public, private, public, private, public, , !, )
+M(21, private, public, private, public, private, public, , !, )
+M(22, public, private, private, public, private, public, , !, )
+M(23, private, private, private, public, private, public, , !, )
+M(24, public, public, public, private, private, public, , , )
+M(25, private, public, public, private, private, public, , , )
+M(26, public, private, public, private, private, public, , , )
+M(27, private, private, public, private, private, public, !, !, !)
+M(28, public, public, private, private, private, public, , !, )
+M(29, private, public, private, private, private, public, , !, )
+M(30, public, private, private, private, private, public, !, !, !)
+M(31, private, private, private, private, private, public, !, !, !)
+M(32, public, public, public, public, public, private, , , !)
+M(33, private, public, public, public, public, private, , , !)
+M(34, public, private, public, public, public, private, , , !)
+M(35, private, private, public, public, public, private, , , !)
+M(36, public, public, private, public, public, private, , , !)
+M(37, private, public, private, public, public, private, , , !)
+M(38, public, private, private, public, public, private, , , !)
+M(39, private, private, private, public, public, private, , , !)
+M(40, public, public, public, private, public, private, , , !)
+M(41, private, public, public, private, public, private, !, !, !)
+M(42, public, private, public, private, public, private, , , !)
+M(43, private, private, public, private, public, private, !, !, !)
+M(44, public, public, private, private, public, private, , , !)
+M(45, private, public, private, private, public, private, !, !, !)
+M(46, public, private, private, private, public, private, , , !)
+M(47, private, private, private, private, public, private, !, !, !)
+M(48, public, public, public, public, private, private, , !, !)
+M(49, private, public, public, public, private, private, , !, !)
+M(50, public, private, public, public, private, private, , !, !)
+M(51, private, private, public, public, private, private, , !, !)
+M(52, public, public, private, public, private, private, , !, !)
+M(53, private, public, private, public, private, private, , !, !)
+M(54, public, private, private, public, private, private, , !, !)
+M(55, private, private, private, public, private, private, , !, !)
+M(56, public, public, public, private, private, private, !, !, !)
+M(57, private, public, public, private, private, private, !, !, !)
+M(58, public, private, public, private, private, private, !, !, !)
+M(59, private, private, public, private, private, private, !, !, !)
+M(60, public, public, private, private, private, private, !, !, !)
+M(61, private, public, private, private, private, private, !, !, !)
+M(62, public, private, private, private, private, private, !, !, !)
+M(63, private, private, private, private, private, private, !, !, !)
#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
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 }
struct V : virtual public S
{
V () : v (0) {}
- constexpr ~V () = default; // { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+ constexpr ~V () = default; // { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" "" { target c++23_down } }
int v;
};
struct W0
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);
struct A
{
virtual void foo(void) {}
+#if __cpp_constexpr_virtual_inheritance >= 202506L
+ A() {}
+#endif
};
struct B: virtual A
{