c++: Handle std::construct_at on automatic vars during constant evaluation [PR97195]

As mentioned in the PR, we only support due to a bug in constant expressions
std::construct_at on non-automatic variables, because we VERIFY_CONSTANT the
second argument of placement new, which fails verification if it is an
address of an automatic variable.
The following patch fixes it by not performing that verification, the
placement new evaluation later on will verify it after it is dereferenced.

2020-10-01  Jakub Jelinek  <jakub@redhat.com>

	PR c++/97195
	* constexpr.c (cxx_eval_call_expression): Don't VERIFY_CONSTANT the
	second argument.

	* g++.dg/cpp2a/constexpr-new14.C: New test.
This commit is contained in:
Jakub Jelinek 2020-10-01 11:16:44 +02:00
parent 85516b7173
commit 2805fcb326
2 changed files with 75 additions and 1 deletions

View File

@ -2342,9 +2342,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
tree arg = CALL_EXPR_ARG (t, i); tree arg = CALL_EXPR_ARG (t, i);
arg = cxx_eval_constant_expression (ctx, arg, false, arg = cxx_eval_constant_expression (ctx, arg, false,
non_constant_p, overflow_p); non_constant_p, overflow_p);
VERIFY_CONSTANT (arg);
if (i == 1) if (i == 1)
arg1 = arg; arg1 = arg;
else
VERIFY_CONSTANT (arg);
} }
gcc_assert (arg1); gcc_assert (arg1);
return arg1; return arg1;

View File

@ -0,0 +1,73 @@
// PR c++/97195
// { dg-do compile { target c++20 } }
namespace std
{
typedef __SIZE_TYPE__ size_t;
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); }
};
template <typename T, typename U = T &&>
U __declval (int);
template <typename T>
T __declval (long);
template <typename T>
auto declval () noexcept -> decltype (__declval<T> (0));
template <typename T>
struct remove_reference
{ typedef T type; };
template <typename T>
struct remove_reference<T &>
{ typedef T type; };
template <typename T>
struct remove_reference<T &&>
{ typedef T type; };
template <typename T>
constexpr T &&
forward (typename std::remove_reference<T>::type &t) noexcept
{ return static_cast<T&&> (t); }
template<typename T>
constexpr T &&
forward (typename std::remove_reference<T>::type &&t) noexcept
{ return static_cast<T&&> (t); }
template <typename T, typename... A>
constexpr auto
construct_at (T *l, A &&... a)
noexcept (noexcept (::new ((void *) 0) T (std::declval<A> ()...)))
-> decltype (::new ((void *) 0) T (std::declval<A> ()...))
{ return ::new ((void *) l) T (std::forward<A> (a)...); }
template <typename T>
constexpr inline void
destroy_at (T *l)
{ l->~T (); }
}
inline void *operator new (std::size_t, void *p) noexcept
{ return p; }
constexpr bool
foo ()
{
int a = 5;
int *p = std::construct_at (&a, -1);
if (p[0] != -1)
throw 1;
return true;
}
constexpr bool b = foo ();