In this testcase, leaving ctx->ctor pointing to the enclosing object meant that evaluating the initializer for the subobject clobbered previous initializers for the enclosing object. So do update ctx->ctor, just don't add it to the enclosing object ctor.
Tested x86_64-pc-linux-gnu, applying to trunk and 12. PR c++/105795 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_bare_aggregate): Always call init_subob_ctx. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/constexpr-aggr-base1.C: New test. --- gcc/cp/constexpr.cc | 7 ++--- .../g++.dg/cpp1z/constexpr-aggr-base1.C | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-aggr-base1.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 45208478c3f..021eaa34920 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -4777,12 +4777,9 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, tree orig_value = value; /* Like in cxx_eval_store_expression, omit entries for empty fields. */ bool no_slot = TREE_CODE (type) == RECORD_TYPE && is_empty_field (index); - if (no_slot) - new_ctx = *ctx; - else - init_subob_ctx (ctx, new_ctx, index, value); + init_subob_ctx (ctx, new_ctx, index, value); int pos_hint = -1; - if (new_ctx.ctor != ctx->ctor) + if (new_ctx.ctor != ctx->ctor && !no_slot) { /* If we built a new CONSTRUCTOR, attach it now so that other initializers can refer to it. */ diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-aggr-base1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-aggr-base1.C new file mode 100644 index 00000000000..06acb4a9f78 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-aggr-base1.C @@ -0,0 +1,27 @@ +// PR c++/105795 +// { dg-do compile { target c++17 } } + +struct empty +{}; + +template <typename T> +struct tuple_holder +{ + [[no_unique_address]] T value; +}; + +struct tuple : tuple_holder<int>, tuple_holder<empty> +{}; + +constexpr auto make_tuple(int&& i, empty&& e) +{ + return tuple{i, e}; +} + +constexpr int foo() +{ + auto tuple = make_tuple(1, empty{}); + return static_cast<const tuple_holder<int>&>(tuple).value; +} + +static_assert (foo() == 1); base-commit: 37e4e7f77d8f7b7e911bf611a0f8edbc3a850c7a -- 2.27.0