On Fri, Jan 17, 2025 at 08:10:24AM -0500, Jason Merrill wrote: > On 1/16/25 8:04 PM, Marek Polacek wrote: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > The recent r15-6369 unfortunately caused a bad wrong-code issue. > > Here we have > > > > TARGET_EXPR <D.2996, (void) (D.2996 = {.status=0, > > .data={._vptr.Foo=&_ZTV3Foo + 16}})> > > > > and call cp_fold_r -> maybe_constant_init with object=D.2996. In > > cxx_eval_outermost_constant_expr we now take the type of the object > > if present. An object can't have type 'void' and so we continue to > > evaluate the initializer. That evaluates into a VOID_CST, meaning > > we disregard the whole initializer, and terrible things ensue. > > In that case, I'd think we want to use the value of 'object' (which should > be in ctx.ctor?) instead of the return value of > cxx_eval_constant_expression.
Ah, I'm sorry I didn't choose that approach. Maybe like this, then? Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- The recent r15-6369 unfortunately caused a bad wrong-code issue. Here we have TARGET_EXPR <D.2996, (void) (D.2996 = {.status=0, .data={._vptr.Foo=&_ZTV3Foo + 16}})> and call cp_fold_r -> maybe_constant_init with object=D.2996. In cxx_eval_outermost_constant_expr we now take the type of the object if present. An object can't have type 'void' and so we continue to evaluate the initializer. That evaluates into a VOID_CST, meaning we disregard the whole initializer, and terrible things ensue. For non-simple TARGET_EXPRs, we should return ctx.ctor rather than the result of cxx_eval_constant_expression. PR c++/118396 PR c++/118523 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_outermost_constant_expr): For non-simple TARGET_EXPRs, return ctx.ctor rather than the result of cxx_eval_constant_expression. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-prvalue4.C: New test. * g++.dg/cpp1y/constexpr-prvalue3.C: New test. --- gcc/cp/constexpr.cc | 5 +++ .../g++.dg/cpp0x/constexpr-prvalue4.C | 33 ++++++++++++++ .../g++.dg/cpp1y/constexpr-prvalue3.C | 45 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue4.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-prvalue3.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index c898e3bfa6e..078f7b19f24 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -8978,6 +8978,11 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, r = cxx_eval_constant_expression (&ctx, r, vc_prvalue, &non_constant_p, &overflow_p); + /* If we got a non-simple TARGET_EXPR, the initializer was a sequence + of statements, and the result ought to be stored in ctx.ctor. */ + if (r == void_node && !constexpr_dtor && ctx.ctor) + r = ctx.ctor; + if (!constexpr_dtor) verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); else diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue4.C new file mode 100644 index 00000000000..afcee65f880 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-prvalue4.C @@ -0,0 +1,33 @@ +// PR c++/118396 +// { dg-do run { target c++11 } } +// { dg-options "-O" } + +void *operator new(__SIZE_TYPE__, void *__p) { return __p; } + +struct Foo { + virtual ~Foo() = default; +}; +struct Data { + int status; + Foo data{}; +}; + +Data *P, *Q; + +struct vector { + vector (const Data &__value) { + P = static_cast<Data *>(__builtin_operator_new(0)); + new (P) Data (__value); + Q = P + 1; + } + Data *begin() { return P; } + Data *end() { return Q; } +}; + +int +main () +{ + vector items_(Data{}); + for (auto item : items_) + item.status == 0 ? void() : __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-prvalue3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-prvalue3.C new file mode 100644 index 00000000000..8ea86c60be5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-prvalue3.C @@ -0,0 +1,45 @@ +// PR c++/118523 +// { dg-do compile { target c++14 } } +// { dg-options "-O2 -Wall" } + +struct __new_allocator { + constexpr __new_allocator() {} + __new_allocator(__new_allocator &) {} +}; +template <typename> using __allocator_base = __new_allocator; +template <typename> struct allocator_traits; +template <typename> struct allocator : __allocator_base<int> {}; +template <typename _Tp> struct allocator_traits<allocator<_Tp>> { + using pointer = _Tp *; + template <typename _Up> using rebind_alloc = allocator<_Up>; + static void deallocate(allocator<_Tp>, pointer, long); +}; +struct __alloc_traits : allocator_traits<allocator<int>> {}; +struct _Vector_impl_data { + __alloc_traits::pointer _M_start; + __alloc_traits::pointer _M_end_of_storage; + constexpr _Vector_impl_data() : _M_start(), _M_end_of_storage() {} +}; +struct _Vector_impl : __alloc_traits::rebind_alloc<int>, _Vector_impl_data {}; +struct _Vector_base { + ~_Vector_base() { + _M_deallocate(_M_impl._M_start, + _M_impl._M_end_of_storage - _M_impl._M_start); + } + _Vector_impl _M_impl; + void _M_deallocate(__alloc_traits::pointer __p, long __n) { + if (__p) + __alloc_traits::deallocate(_M_impl, __p, __n); + } +}; +struct vector : protected _Vector_base {}; +struct S { + vector a{}; +}; +struct B2 { + B2(S); +}; +struct E : B2 { + E(S opts = {}) : B2{opts} {} +}; +void fun() { E{}; } base-commit: 71280df05f28fd568f8b863dd700c0be2f2443a8 -- 2.48.0