c++: Fix ICE on constexpr placement new [PR115754]

C++26 is making in P2747R2 paper placement new constexpr.
While working on a patch for that, I've noticed we ICE starting with
GCC 14 on the following testcase.
The problem is that e.g. for the void * to sometype * casts checks,
we really assume the casts have their operand constant evaluated
as prvalue, but on the testcase the cast itself is evaluated with
vc_discard and that means op can end up e.g. a VAR_DECL which the
later code doesn't like and asserts on.
If the result type is void, we don't really need the cast operand
for anything, so can use vc_discard for the recursive call,
VIEW_CONVERT_EXPR can appear on the lhs, so we need to honor the
lval but otherwise the patch uses vc_prvalue.
I'd like to get this patch in before the rest of P2747R2 implementation,
so that it can be backported to 14.2 later on.

2024-07-02  Jakub Jelinek  <jakub@redhat.com>
	    Jason Merrill  <jason@redhat.com>

	PR c++/115754
	* constexpr.cc (cxx_eval_constant_expression) <case CONVERT_EXPR>:
	For conversions to void, pass vc_discard to the recursive call
	and otherwise for tcode other than VIEW_CONVERT_EXPR pass vc_prvalue.

	* g++.dg/cpp26/pr115754.C: New test.
This commit is contained in:
Jakub Jelinek 2024-07-02 22:09:58 +02:00
parent beb7a418aa
commit 1250540a98
2 changed files with 40 additions and 1 deletions

View file

@ -8103,7 +8103,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
tree oldop = TREE_OPERAND (t, 0);
tree op = cxx_eval_constant_expression (ctx, oldop,
lval,
VOID_TYPE_P (TREE_TYPE (t))
? vc_discard
: tcode == VIEW_CONVERT_EXPR
? lval : vc_prvalue,
non_constant_p, overflow_p);
if (*non_constant_p)
return t;

View file

@ -0,0 +1,36 @@
// PR c++/115754
// { dg-do compile { target c++26 } }
namespace std
{
using size_t = decltype (sizeof 0);
template <typename T>
struct allocator
{
constexpr allocator () noexcept {}
constexpr T *allocate (size_t n)
{ return static_cast<T *> (::operator new (n * sizeof(T))); }
constexpr void
deallocate (T *p, size_t n)
{ ::operator delete (p); }
};
}
constexpr void *
operator new (std::size_t, void *p) noexcept
{ return p; }
constexpr bool
foo ()
{
std::allocator<int> a;
auto b = a.allocate (1);
::new (b) int ();
a.deallocate (b, 1);
return true;
}
constexpr bool a = foo ();