rsmith updated this revision to Diff 222943.
rsmith added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Moved some changes from the vector patch to here.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D68364/new/

https://reviews.llvm.org/D68364

Files:
  clang/include/clang/Basic/DiagnosticASTKinds.td
  clang/lib/AST/ExprConstant.cpp
  clang/lib/AST/Interp/State.h
  clang/lib/Frontend/InitPreprocessor.cpp
  clang/test/Lexer/cxx-features.cpp
  clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
  clang/www/cxx_status.html
  libcxx/include/__config
  libcxx/include/memory
  libcxx/include/new
  libcxx/include/version

Index: libcxx/include/version
===================================================================
--- libcxx/include/version
+++ libcxx/include/version
@@ -218,6 +218,9 @@
 #   define __cpp_lib_char8_t                            201811L
 # endif
 // # define __cpp_lib_concepts                             201806L
+# if !defined(_LIBCPP_HAS_NO_CONSTEXPR_DYNAMIC_ALLOC)
+#  define __cpp_lib_constexpr_dynamic_alloc                201907L
+# endif
 // # define __cpp_lib_constexpr_misc                       201811L
 // # define __cpp_lib_constexpr_swap_algorithms            201806L
 # if _LIBCPP_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L
Index: libcxx/include/new
===================================================================
--- libcxx/include/new
+++ libcxx/include/new
@@ -234,7 +234,8 @@
 #endif
 }
 
-inline _LIBCPP_INLINE_VISIBILITY void *__libcpp_allocate(size_t __size, size_t __align) {
+_LIBCPP_CONSTEXPR_DYNAMIC_ALLOC inline _LIBCPP_INLINE_VISIBILITY
+void *__libcpp_allocate(size_t __size, size_t __align) {
 #ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
   if (__is_overaligned_for_new(__align)) {
     const align_val_t __align_val = static_cast<align_val_t>(__align);
@@ -255,7 +256,7 @@
 }
 
 struct _DeallocateCaller {
-  static inline _LIBCPP_INLINE_VISIBILITY
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline _LIBCPP_INLINE_VISIBILITY
   void __do_deallocate_handle_size_align(void *__ptr, size_t __size, size_t __align) {
 #if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
     ((void)__align);
@@ -270,7 +271,7 @@
 #endif
   }
 
-  static inline _LIBCPP_INLINE_VISIBILITY
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline _LIBCPP_INLINE_VISIBILITY
   void __do_deallocate_handle_align(void *__ptr, size_t __align) {
 #if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
     ((void)__align);
@@ -286,7 +287,7 @@
   }
 
  private:
-  static inline void __do_deallocate_handle_size(void *__ptr, size_t __size) {
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_deallocate_handle_size(void *__ptr, size_t __size) {
 #ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
     ((void)__size);
     return __do_call(__ptr);
@@ -296,7 +297,7 @@
   }
 
 #ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
-  static inline void __do_deallocate_handle_size(void *__ptr, size_t __size, align_val_t __align) {
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_deallocate_handle_size(void *__ptr, size_t __size, align_val_t __align) {
 #ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
     ((void)__size);
     return __do_call(__ptr, __align);
@@ -308,7 +309,7 @@
 
 private:
   template <class _A1, class _A2>
-  static inline void __do_call(void *__ptr, _A1 __a1, _A2 __a2) {
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_call(void *__ptr, _A1 __a1, _A2 __a2) {
 #if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \
     defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE)
     return ::operator delete(__ptr, __a1, __a2);
@@ -318,7 +319,7 @@
   }
 
   template <class _A1>
-  static inline void __do_call(void *__ptr, _A1 __a1) {
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_call(void *__ptr, _A1 __a1) {
 #if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \
     defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE)
     return ::operator delete(__ptr, __a1);
@@ -327,7 +328,7 @@
 #endif
   }
 
-  static inline void __do_call(void *__ptr) {
+  _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC static inline void __do_call(void *__ptr) {
 #ifdef _LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE
     return ::operator delete(__ptr);
 #else
@@ -336,11 +337,13 @@
   }
 };
 
-inline _LIBCPP_INLINE_VISIBILITY void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) {
+_LIBCPP_CONSTEXPR_DYNAMIC_ALLOC inline _LIBCPP_INLINE_VISIBILITY
+void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) {
   _DeallocateCaller::__do_deallocate_handle_size_align(__ptr, __size, __align);
 }
 
-inline _LIBCPP_INLINE_VISIBILITY void __libcpp_deallocate_unsized(void* __ptr, size_t __align) {
+_LIBCPP_CONSTEXPR_DYNAMIC_ALLOC inline _LIBCPP_INLINE_VISIBILITY
+void __libcpp_deallocate_unsized(void* __ptr, size_t __align) {
   _DeallocateCaller::__do_deallocate_handle_align(__ptr, __align);
 }
 
Index: libcxx/include/memory
===================================================================
--- libcxx/include/memory
+++ libcxx/include/memory
@@ -1565,47 +1565,47 @@
         {typedef allocator_traits<typename rebind_alloc<_Tp>::other> other;};
 #endif  // _LIBCPP_CXX03_LANG
 
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static pointer allocate(allocator_type& __a, size_type __n)
         {return __a.allocate(__n);}
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static pointer allocate(allocator_type& __a, size_type __n, const_void_pointer __hint)
         {return __allocate(__a, __n, __hint,
             __has_allocate_hint<allocator_type, size_type, const_void_pointer>());}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static void deallocate(allocator_type& __a, pointer __p, size_type __n) _NOEXCEPT
         {__a.deallocate(__p, __n);}
 
 #ifndef _LIBCPP_HAS_NO_VARIADICS
     template <class _Tp, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void construct(allocator_type& __a, _Tp* __p, _Args&&... __args)
             {__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
                          __a, __p, _VSTD::forward<_Args>(__args)...);}
 #else  // _LIBCPP_HAS_NO_VARIADICS
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void construct(allocator_type&, _Tp* __p)
             {
                 ::new ((void*)__p) _Tp();
             }
     template <class _Tp, class _A0>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void construct(allocator_type& __a, _Tp* __p, const _A0& __a0)
             {
                 __construct(__has_construct<allocator_type, _Tp*, const _A0&>(),
                             __a, __p, __a0);
             }
     template <class _Tp, class _A0, class _A1>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void construct(allocator_type&, _Tp* __p, const _A0& __a0,
                               const _A1& __a1)
             {
                 ::new ((void*)__p) _Tp(__a0, __a1);
             }
     template <class _Tp, class _A0, class _A1, class _A2>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void construct(allocator_type&, _Tp* __p, const _A0& __a0,
                               const _A1& __a1, const _A2& __a2)
             {
@@ -1614,15 +1614,15 @@
 #endif  // _LIBCPP_HAS_NO_VARIADICS
 
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void destroy(allocator_type& __a, _Tp* __p)
             {__destroy(__has_destroy<allocator_type, _Tp*>(), __a, __p);}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static size_type max_size(const allocator_type& __a) _NOEXCEPT
         {return __max_size(__has_max_size<const allocator_type>(), __a);}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static allocator_type
         select_on_container_copy_construction(const allocator_type& __a)
             {return __select_on_container_copy_construction(
@@ -1630,7 +1630,7 @@
                 __a);}
 
     template <class _Ptr>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static
         void
         __construct_forward_with_exception_guarantees(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __begin2)
@@ -1648,7 +1648,7 @@
         }
 
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static
         typename enable_if
         <
@@ -1657,18 +1657,32 @@
              is_trivially_move_constructible<_Tp>::value,
             void
         >::type
-        __construct_forward_with_exception_guarantees(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __begin2)
+        __construct_forward_with_exception_guarantees(allocator_type& __a, _Tp* __begin1, _Tp* __end1, _Tp*& __begin2)
         {
             ptrdiff_t _Np = __end1 - __begin1;
             if (_Np > 0)
             {
-                _VSTD::memcpy(__begin2, __begin1, _Np * sizeof(_Tp));
-                __begin2 += _Np;
+#ifndef _LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED
+                if (__builtin_is_constant_evaluated())
+                    for (; __begin1 != __end1; ++__begin1, (void) ++__begin2)
+                      construct(__a, _VSTD::__to_raw_pointer(__begin2),
+#ifdef _LIBCPP_NO_EXCEPTIONS
+                                _VSTD::move(*__begin1)
+#else
+                                _VSTD::move_if_noexcept(*__begin1)
+#endif
+                                );
+                else
+#endif
+                {
+                    _VSTD::memcpy(__begin2, __begin1, _Np * sizeof(_Tp));
+                    __begin2 += _Np;
+                }
             }
         }
 
     template <class _Iter, class _Ptr>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static
         void
         __construct_range_forward(allocator_type& __a, _Iter __begin1, _Iter __end1, _Ptr& __begin2)
@@ -1680,7 +1694,7 @@
     template <class _SourceTp, class _DestTp,
               class _RawSourceTp = typename remove_const<_SourceTp>::type,
               class _RawDestTp = typename remove_const<_DestTp>::type>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static
         typename enable_if
         <
@@ -1690,18 +1704,26 @@
              !__has_construct<allocator_type, _DestTp*, _SourceTp&>::value),
             void
         >::type
-        __construct_range_forward(allocator_type&, _SourceTp* __begin1, _SourceTp* __end1, _DestTp*& __begin2)
+        __construct_range_forward(allocator_type& __a, _SourceTp* __begin1, _SourceTp* __end1, _DestTp*& __begin2)
         {
             ptrdiff_t _Np = __end1 - __begin1;
             if (_Np > 0)
             {
-                _VSTD::memcpy(const_cast<_RawDestTp*>(__begin2), __begin1, _Np * sizeof(_DestTp));
-                __begin2 += _Np;
+#ifndef _LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED
+                if (__builtin_is_constant_evaluated())
+                    for (; __begin1 != __end1; ++__begin1, (void) ++__begin2)
+                        construct(__a, _VSTD::__to_raw_pointer(__begin2), *__begin1);
+                else
+#endif
+                {
+                    _VSTD::memcpy(const_cast<_RawDestTp*>(__begin2), __begin1, _Np * sizeof(_DestTp));
+                    __begin2 += _Np;
+                }
             }
         }
 
     template <class _Ptr>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static
         void
         __construct_backward_with_exception_guarantees(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __end2)
@@ -1722,7 +1744,7 @@
         }
 
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static
         typename enable_if
         <
@@ -1731,44 +1753,65 @@
              is_trivially_move_constructible<_Tp>::value,
             void
         >::type
-        __construct_backward_with_exception_guarantees(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __end2)
+        __construct_backward_with_exception_guarantees(allocator_type& __a, _Tp* __begin1, _Tp* __end1, _Tp*& __end2)
         {
             ptrdiff_t _Np = __end1 - __begin1;
-            __end2 -= _Np;
             if (_Np > 0)
-                _VSTD::memcpy(__end2, __begin1, _Np * sizeof(_Tp));
+            {
+#ifndef _LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED
+                if (__builtin_is_constant_evaluated())
+                {
+                    while (__end1 != __begin1)
+                    {
+                        construct(__a, _VSTD::__to_raw_pointer(__end2 - 1),
+#ifdef _LIBCPP_NO_EXCEPTIONS
+                                  _VSTD::move(*--__end1)
+#else
+                                  _VSTD::move_if_noexcept(*--__end1)
+#endif
+                                  );
+                        --__end2;
+                    }
+                }
+                else
+#endif
+                {
+                    __end2 -= _Np;
+                    _VSTD::memcpy(__end2, __begin1, _Np * sizeof(_Tp));
+                }
+            }
         }
 
 private:
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static pointer __allocate(allocator_type& __a, size_type __n,
         const_void_pointer __hint, true_type)
         {return __a.allocate(__n, __hint);}
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static pointer __allocate(allocator_type& __a, size_type __n,
         const_void_pointer, false_type)
         {return __a.allocate(__n);}
 
 #ifndef _LIBCPP_HAS_NO_VARIADICS
     template <class _Tp, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void __construct(true_type, allocator_type& __a, _Tp* __p, _Args&&... __args)
             {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
     template <class _Tp, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void __construct(false_type, allocator_type&, _Tp* __p, _Args&&... __args)
             {
                 ::new ((void*)__p) _Tp(_VSTD::forward<_Args>(__args)...);
             }
 #else  // _LIBCPP_HAS_NO_VARIADICS
     template <class _Tp, class _A0>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void __construct(true_type, allocator_type& __a, _Tp* __p,
                                 const _A0& __a0)
             {__a.construct(__p, __a0);}
     template <class _Tp, class _A0>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void __construct(false_type, allocator_type&, _Tp* __p,
                                 const _A0& __a0)
             {
@@ -1777,28 +1820,28 @@
 #endif  // _LIBCPP_HAS_NO_VARIADICS
 
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void __destroy(true_type, allocator_type& __a, _Tp* __p)
             {__a.destroy(__p);}
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         static void __destroy(false_type, allocator_type&, _Tp* __p)
             {
                 __p->~_Tp();
             }
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static size_type __max_size(true_type, const allocator_type& __a) _NOEXCEPT
             {return __a.max_size();}
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static size_type __max_size(false_type, const allocator_type&) _NOEXCEPT
             {return numeric_limits<size_type>::max() / sizeof(value_type);}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static allocator_type
         __select_on_container_copy_construction(true_type, const allocator_type& __a)
             {return __a.select_on_container_copy_construction();}
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     static allocator_type
         __select_on_container_copy_construction(false_type, const allocator_type& __a)
             {return __a;}
@@ -1844,7 +1887,7 @@
         {return _VSTD::addressof(__x);}
     _LIBCPP_INLINE_VISIBILITY const_pointer address(const_reference __x) const _NOEXCEPT
         {return _VSTD::addressof(__x);}
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
     pointer allocate(size_type __n, allocator<void>::const_pointer = 0)
         {
         if (__n > max_size())
@@ -1852,13 +1895,14 @@
                                  " 'n' exceeds maximum supported size");
         return static_cast<pointer>(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
         }
-    _LIBCPP_INLINE_VISIBILITY void deallocate(pointer __p, size_type __n) _NOEXCEPT
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
+    void deallocate(pointer __p, size_type __n) _NOEXCEPT
         {_VSTD::__libcpp_deallocate((void*)__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));}
-    _LIBCPP_INLINE_VISIBILITY size_type max_size() const _NOEXCEPT
+    _LIBCPP_CONSTEXPR_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY size_type max_size() const _NOEXCEPT
         {return size_type(~0) / sizeof(_Tp);}
 #if !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) && !defined(_LIBCPP_HAS_NO_VARIADICS)
     template <class _Up, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
         void
         construct(_Up* __p, _Args&&... __args)
         {
@@ -1917,7 +1961,8 @@
             ::new((void*)__p) _Tp(__a0, __a1);
         }
 #endif  // !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES) && !defined(_LIBCPP_HAS_NO_VARIADICS)
-    _LIBCPP_INLINE_VISIBILITY void destroy(pointer __p) {__p->~_Tp();}
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC _LIBCPP_INLINE_VISIBILITY
+    void destroy(pointer __p) {__p->~_Tp();}
 };
 
 template <class _Tp>
@@ -1946,6 +1991,7 @@
 
     _LIBCPP_INLINE_VISIBILITY const_pointer address(const_reference __x) const _NOEXCEPT
         {return _VSTD::addressof(__x);}
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC
     _LIBCPP_INLINE_VISIBILITY pointer allocate(size_type __n, allocator<void>::const_pointer = 0)
     {
         if (__n > max_size())
@@ -1953,6 +1999,7 @@
                                  " 'n' exceeds maximum supported size");
         return static_cast<pointer>(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
     }
+    _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC
     _LIBCPP_INLINE_VISIBILITY void deallocate(pointer __p, size_type __n) _NOEXCEPT
         {_VSTD::__libcpp_deallocate((void*) const_cast<_Tp *>(__p), __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));}
     _LIBCPP_INLINE_VISIBILITY size_type max_size() const _NOEXCEPT
@@ -2022,11 +2069,11 @@
 };
 
 template <class _Tp, class _Up>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC
 bool operator==(const allocator<_Tp>&, const allocator<_Up>&) _NOEXCEPT {return true;}
 
 template <class _Tp, class _Up>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC
 bool operator!=(const allocator<_Tp>&, const allocator<_Up>&) _NOEXCEPT {return false;}
 
 template <class _OutputIterator, class _Tp>
Index: libcxx/include/__config
===================================================================
--- libcxx/include/__config
+++ libcxx/include/__config
@@ -575,6 +575,10 @@
 
 #endif // _LIBCPP_COMPILER_[CLANG|GCC|MSVC|IBM]
 
+#ifndef __cpp_constexpr_dynamic_alloc
+#define _LIBCPP_HAS_NO_CONSTEXPR_DYNAMIC_ALLOC
+#endif
+
 #if defined(_LIBCPP_OBJECT_FORMAT_COFF)
 
 #ifdef _DLL
@@ -987,6 +991,12 @@
 #  define _LIBCPP_CONSTEXPR_AFTER_CXX17
 #endif
 
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONSTEXPR_DYNAMIC_ALLOC)
+#  define _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC constexpr
+#else
+#  define _LIBCPP_CONSTEXPR_DYNAMIC_ALLOC
+#endif
+
 // The _LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other
 // NODISCARD macros to the correct attribute.
 #if __has_cpp_attribute(nodiscard) || defined(_LIBCPP_COMPILER_MSVC)
Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -1003,14 +1003,13 @@
       </tr>
       <tr> <!-- from Cologne -->
         <td><a href="http://wg21.link/p1331r2";>P1331R2</a></td>
-        <td rowspan="2" class="svn" align="center">SVN</td>
+        <td rowspan="3" class="svn" align="center">SVN</td>
       </tr>
       <tr>
         <td><a href="http://wg21.link/p1668r1";>P1668R1</a></td>
       </tr>
       <tr>
         <td><a href="http://wg21.link/p0784r7";>P0784R7</a></td>
-        <td class="partial" align="center">Partial</td>
       </tr>
     <tr>
       <td>Prohibit aggregates with user-declared constructors</td>
Index: clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
@@ -0,0 +1,168 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s -DNEW=__builtin_operator_new -DDELETE=__builtin_operator_delete
+// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=operator new" "-DDELETE=operator delete"
+// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=::operator new" "-DDELETE=::operator delete"
+
+constexpr bool alloc_from_user_code() {
+  void *p = NEW(sizeof(int)); // expected-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator<T>::allocate'}}
+  DELETE(p);
+  return true;
+}
+static_assert(alloc_from_user_code()); // expected-error {{constant expression}} expected-note {{in call}}
+
+namespace std {
+  using size_t = decltype(sizeof(0));
+  // FIXME: It would be preferable to point these notes at the location of the call to allocator<...>::[de]allocate instead
+  template<typename T> struct allocator {
+    constexpr T *allocate(size_t N) {
+      return (T*)NEW(sizeof(T) * N); // expected-note 3{{heap allocation}} expected-note {{not deallocated}}
+    }
+    constexpr void deallocate(void *p) {
+      DELETE(p); // expected-note 2{{'std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}}
+    }
+  };
+}
+
+constexpr bool alloc_via_std_allocator() {
+  std::allocator<int> alloc;
+  int *p = alloc.allocate(1);
+  alloc.deallocate(p);
+  return true;
+}
+static_assert(alloc_via_std_allocator());
+
+template<> struct std::allocator<void()> {
+  constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of function type 'void ()'}}
+};
+constexpr void *fn = std::allocator<void()>().allocate(); // expected-error {{constant expression}} expected-note {{in call}}
+
+struct Incomplete;
+template<> struct std::allocator<Incomplete> {
+  constexpr void *allocate() { return NEW(8); } // expected-note {{cannot allocate memory of incomplete type 'Incomplete'}}
+};
+constexpr void *incomplete = std::allocator<Incomplete>().allocate(); // expected-error {{constant expression}} expected-note {{in call}}
+
+struct WrongSize { char x[5]; };
+static_assert(sizeof(WrongSize) == 5);
+template<> struct std::allocator<WrongSize> {
+  constexpr void *allocate() { return NEW(7); } // expected-note {{allocated size 7 is not a multiple of size 5 of element type 'WrongSize'}}
+};
+constexpr void *wrong_size = std::allocator<WrongSize>().allocate(); // expected-error {{constant expression}} expected-note {{in call}}
+
+constexpr bool mismatched(int alloc_kind, int dealloc_kind) {
+  int *p;
+  switch (alloc_kind) {
+  case 0:
+    p = new int; // expected-note {{heap allocation}}
+    break;
+  case 1:
+    p = new int[1]; // expected-note {{heap allocation}}
+    break;
+  case 2:
+    p = std::allocator<int>().allocate(1);
+    break;
+  }
+  switch (dealloc_kind) {
+  case 0:
+    delete p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}}
+    break;
+  case 1:
+    delete[] p; // expected-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}}
+    break;
+  case 2:
+    std::allocator<int>().deallocate(p); // expected-note 2{{in call}}
+    break;
+  }
+  return true;
+}
+static_assert(mismatched(0, 2)); // expected-error {{constant expression}} expected-note {{in call}}
+static_assert(mismatched(1, 2)); // expected-error {{constant expression}} expected-note {{in call}}
+static_assert(mismatched(2, 0)); // expected-error {{constant expression}} expected-note {{in call}}
+static_assert(mismatched(2, 1)); // expected-error {{constant expression}} expected-note {{in call}}
+static_assert(mismatched(2, 2));
+
+constexpr int *escape = std::allocator<int>().allocate(3); // expected-error {{constant expression}} expected-note {{pointer to subobject of heap-allocated}}
+constexpr int leak = (std::allocator<int>().allocate(3), 0); // expected-error {{constant expression}}
+constexpr int no_lifetime_start = (*std::allocator<int>().allocate(1) = 1); // expected-error {{constant expression}} expected-note {{assignment to object outside its lifetime}}
+
+void *operator new(std::size_t, void *p) { return p; }
+constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
+  int a;
+  new (&a) int(42); // expected-note {{call to placement 'operator new'}}
+  return a == 42;
+}
+
+namespace std {
+  constexpr bool placement_new_in_stdlib() {
+    int a;
+    new (&a) int(42);
+    return a == 42;
+  }
+}
+static_assert(std::placement_new_in_stdlib());
+
+namespace std {
+  template<typename T, typename ...Args>
+  constexpr void construct_at(void *p, Args &&...args) {
+    new (p) T((Args&&)args...); // #new
+  }
+}
+
+constexpr bool call_std_construct_at() {
+  int *p = std::allocator<int>().allocate(3);
+  std::construct_at<int>(p, 1);
+  std::construct_at<int>(p + 1, 2);
+  std::construct_at<int>(p + 2, 3);
+  bool good = p[0] + p[1] + p[2] == 6;
+  std::allocator<int>().deallocate(p);
+  return good;
+}
+static_assert(call_std_construct_at());
+
+constexpr bool bad_construct_at_type() {
+  int a;
+  // expected-note@#new {{placement new would change type of storage from 'int' to 'float'}}
+  std::construct_at<float>(&a, 1.0f); // expected-note {{in call}}
+  return true;
+}
+static_assert(bad_construct_at_type()); // expected-error{{}} expected-note {{in call}}
+
+constexpr bool bad_construct_at_subobject() {
+  struct X { int a, b; };
+  union A {
+    int a;
+    X x;
+  };
+  A a = {1};
+  // expected-note@#new {{construction of subobject of member 'x' of union with active member 'a' is not allowed in a constant expression}}
+  std::construct_at<int>(&a.x.a, 1); // expected-note {{in call}}
+  return true;
+}
+static_assert(bad_construct_at_subobject()); // expected-error{{}} expected-note {{in call}}
+
+constexpr bool change_union_member() {
+  union U {
+    int a;
+    int b;
+  };
+  U u = {.a = 1};
+  std::construct_at<int>(&u.b, 2);
+  return u.b == 2;
+}
+static_assert(change_union_member());
+
+int external;
+// expected-note@#new {{visible outside}}
+static_assert((std::construct_at<int>(&external, 1), true)); // expected-error{{}} expected-note {{in call}}
+
+constexpr int &&temporary = 0; // expected-note {{created here}}
+// expected-note@#new {{construction of temporary is not allowed in a constant expression outside the expression that created the temporary}}
+static_assert((std::construct_at<int>(&temporary, 1), true)); // expected-error{{}} expected-note {{in call}}
+
+constexpr bool construct_after_lifetime() {
+  int *p = new int;
+  delete p;
+  // expected-note@#new {{construction of heap allocated object that has been deleted}}
+  std::construct_at<int>(p); // expected-note {{in call}}
+  return true;
+}
+static_assert(construct_after_lifetime()); // expected-error {{}} expected-note {{in call}}
Index: clang/test/Lexer/cxx-features.cpp
===================================================================
--- clang/test/Lexer/cxx-features.cpp
+++ clang/test/Lexer/cxx-features.cpp
@@ -34,6 +34,10 @@
 #error "wrong value for __cpp_char8_t"
 #endif
 
+#if check(constexpr_dynamic_alloc, 0, 0, 0, 0, 201907)
+#error "wrong value for __cpp_constexpr_dynamic_alloc"
+#endif
+
 #if check(constinit, 0, 0, 0, 0, 201907)
 #error "wrong value for __cpp_constinit"
 #endif
Index: clang/lib/Frontend/InitPreprocessor.cpp
===================================================================
--- clang/lib/Frontend/InitPreprocessor.cpp
+++ clang/lib/Frontend/InitPreprocessor.cpp
@@ -543,6 +543,7 @@
   // C++20 features.
   if (LangOpts.CPlusPlus2a) {
     Builder.defineMacro("__cpp_conditional_explicit", "201806L");
+    Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L");
     Builder.defineMacro("__cpp_constinit", "201907L");
   }
   if (LangOpts.Char8)
Index: clang/lib/AST/Interp/State.h
===================================================================
--- clang/lib/AST/Interp/State.h
+++ clang/lib/AST/Interp/State.h
@@ -32,6 +32,7 @@
   AK_MemberCall,
   AK_DynamicCast,
   AK_TypeId,
+  AK_Construct,
   AK_Destroy,
 };
 
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -594,6 +594,13 @@
     Frame *getCaller() const override { return Caller; }
     SourceLocation getCallLocation() const override { return CallLoc; }
     const FunctionDecl *getCallee() const override { return Callee; }
+
+    bool isStdFunction() const {
+      for (const DeclContext *DC = Callee; DC; DC = DC->getParent())
+        if (DC->isStdNamespace())
+          return true;
+      return false;
+    }
   };
 
   /// Temporarily override 'this'.
@@ -690,6 +697,37 @@
 }
 
 namespace {
+  /// A dynamically-allocated heap object.
+  struct DynAlloc {
+    /// The value of this heap-allocated object.
+    APValue Value;
+    /// The allocating expression; used for diagnostics. Either a CXXNewExpr
+    /// or a CallExpr (the latter is for direct calls to operator new inside
+    /// std::allocator<T>::allocate).
+    const Expr *AllocExpr = nullptr;
+
+    enum Kind {
+      New,
+      ArrayNew,
+      StdAllocator
+    };
+
+    /// Get the kind of the allocation. This must match between allocation
+    /// and deallocation.
+    Kind getKind() const {
+      if (auto *NE = dyn_cast<CXXNewExpr>(AllocExpr))
+        return NE->isArray() ? ArrayNew : New;
+      assert(isa<CallExpr>(AllocExpr));
+      return StdAllocator;
+    }
+  };
+
+  struct DynAllocOrder {
+    bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const {
+      return L.getIndex() < R.getIndex();
+    }
+  };
+
   /// EvalInfo - This is a private struct used by the evaluator to capture
   /// information about a subexpression as it is folded.  It retains information
   /// about the AST context, but also maintains information about the folded
@@ -761,20 +799,6 @@
     llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase>
         ObjectsUnderConstruction;
 
-    /// A dynamically-allocated heap object.
-    struct DynAlloc {
-      /// The value of this heap-allocated object.
-      APValue Value;
-      /// The allocating expression; used for diagnostics.
-      const Expr *AllocExpr = nullptr;
-    };
-
-    struct DynAllocOrder {
-      bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const {
-        return L.getIndex() < R.getIndex();
-      }
-    };
-
     /// Current heap allocations, along with the location where each was
     /// allocated. We use std::map here because we need stable addresses
     /// for the stored APValues.
@@ -970,6 +994,39 @@
       return Result;
     }
 
+    /// Information about a stack frame for std::allocator<T>::[de]allocate.
+    struct StdAllocatorCaller {
+      unsigned FrameIndex;
+      QualType ElemType;
+      explicit operator bool() const { return FrameIndex != 0; };
+    };
+
+    StdAllocatorCaller getStdAllocatorCaller(StringRef FnName) const {
+      for (const CallStackFrame *Call = CurrentCall; Call != &BottomFrame;
+           Call = Call->Caller) {
+        const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Call->Callee);
+        if (!MD)
+          continue;
+        const IdentifierInfo *FnII = MD->getIdentifier();
+        if (!FnII || !FnII->isStr(FnName))
+          continue;
+
+        const auto *CTSD =
+            dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
+        if (!CTSD)
+          continue;
+
+        const IdentifierInfo *ClassII = CTSD->getIdentifier();
+        const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
+        if (CTSD->isInStdNamespace() && ClassII &&
+            ClassII->isStr("allocator") && TAL.size() >= 1 &&
+            TAL[0].getKind() == TemplateArgument::Type)
+          return {Call->Index, TAL[0].getAsType()};
+      }
+
+      return {};
+    }
+
     void performLifetimeExtension() {
       // Disable the cleanups for lifetime-extended temporaries.
       CleanupStack.erase(
@@ -1345,6 +1402,7 @@
   case AK_Assign:
   case AK_Increment:
   case AK_Decrement:
+  case AK_Construct:
   case AK_Destroy:
     return true;
   }
@@ -1357,7 +1415,7 @@
 
 /// Is this an access per the C++ definition?
 static bool isFormalAccess(AccessKinds AK) {
-  return isAnyAccess(AK) && AK != AK_Destroy;
+  return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy;
 }
 
 namespace {
@@ -1453,9 +1511,10 @@
       IsNullPtr = false;
     }
 
-    void setNull(QualType PointerTy, uint64_t TargetVal) {
+    void setNull(ASTContext &Ctx, QualType PointerTy) {
       Base = (Expr *)nullptr;
-      Offset = CharUnits::fromQuantity(TargetVal);
+      Offset =
+          CharUnits::fromQuantity(Ctx.getTargetNullPointerValue(PointerTy));
       InvalidBase = false;
       Designator = SubobjectDesignator(PointerTy->getPointeeType());
       IsNullPtr = true;
@@ -1465,6 +1524,12 @@
       set(B, true);
     }
 
+    std::string toString(ASTContext &Ctx, QualType T) const {
+      APValue Printable;
+      moveInto(Printable);
+      return Printable.getAsString(Ctx, T);
+    }
+
   private:
     // Check that this LValue is not based on a null pointer. If it is, produce
     // a diagnostic and mark the designator as invalid.
@@ -1905,7 +1970,7 @@
     Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here);
   else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
     // FIXME: Produce a note for dangling pointers too.
-    if (Optional<EvalInfo::DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA))
+    if (Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA))
       Info.Note((*Alloc)->AllocExpr->getExprLoc(),
                 diag::note_constexpr_dynamic_alloc_here);
   }
@@ -3113,8 +3178,9 @@
   // Walk the designator's path to find the subobject.
   for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
     // Reading an indeterminate value is undefined, but assigning over one is OK.
-    if (O->isAbsent() ||
-        (O->isIndeterminate() && handler.AccessKind != AK_Assign &&
+    if ((O->isAbsent() && handler.AccessKind != AK_Construct) ||
+        (O->isIndeterminate() && handler.AccessKind != AK_Construct &&
+         handler.AccessKind != AK_Assign &&
          handler.AccessKind != AK_ReadObjectRepresentation)) {
       if (!Info.checkingPotentialConstantExpression())
         Info.FFDiag(E, diag::note_constexpr_access_uninit)
@@ -3254,13 +3320,18 @@
         const FieldDecl *UnionField = O->getUnionField();
         if (!UnionField ||
             UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) {
-          // FIXME: If O->getUnionValue() is absent, report that there's no
-          // active union member rather than reporting the prior active union
-          // member. We'll need to fix nullptr_t to not use APValue() as its
-          // representation first.
-          Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member)
-            << handler.AccessKind << Field << !UnionField << UnionField;
-          return handler.failed();
+          if (I == N - 1 && handler.AccessKind == AK_Construct) {
+            // Placement new onto an inactive union member makes it active.
+            O->setUnion(Field, APValue());
+          } else {
+            // FIXME: If O->getUnionValue() is absent, report that there's no
+            // active union member rather than reporting the prior active union
+            // member. We'll need to fix nullptr_t to not use APValue() as its
+            // representation first.
+            Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member)
+                << handler.AccessKind << Field << !UnionField << UnionField;
+            return handler.failed();
+          }
         }
         O = &O->getUnionValue();
       } else
@@ -3560,7 +3631,7 @@
     if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal))
       return CompleteObject();
   } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) {
-    Optional<EvalInfo::DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA);
+    Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA);
     if (!Alloc) {
       Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK;
       return CompleteObject();
@@ -5147,8 +5218,7 @@
     if (!E->isGLValue()) {
       //   The value of a failed cast to pointer type is the null pointer value
       //   of the required result type.
-      auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType());
-      Ptr.setNull(E->getType(), TargetVal);
+      Ptr.setNull(Info.Ctx, E->getType());
       return true;
     }
 
@@ -5878,6 +5948,161 @@
   return HandleDestructionImpl(Info, Loc, LV, Value, T);
 }
 
+/// Perform a call to 'perator new' or to `__builtin_operator_new'.
+static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E,
+                                  LValue &Result) {
+  if (Info.checkingPotentialConstantExpression() ||
+      Info.SpeculativeEvaluationDepth)
+    return false;
+
+  // This is permitted only within a call to std::allocator<T>::allocate.
+  auto Caller = Info.getStdAllocatorCaller("allocate");
+  if (!Caller) {
+    Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus2a
+                                     ? diag::note_constexpr_new_untyped
+                                     : diag::note_constexpr_new);
+    return false;
+  }
+
+  QualType ElemType = Caller.ElemType;
+  if (ElemType->isIncompleteType() || ElemType->isFunctionType()) {
+    Info.FFDiag(E->getExprLoc(),
+                diag::note_constexpr_new_not_complete_object_type)
+        << (ElemType->isIncompleteType() ? 0 : 1) << ElemType;
+    return false;
+  }
+
+  APSInt ByteSize;
+  if (!EvaluateInteger(E->getArg(0), ByteSize, Info))
+    return false;
+  bool IsNothrow = false;
+  for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) {
+    EvaluateIgnoredValue(Info, E->getArg(I));
+    IsNothrow |= E->getType()->isNothrowT();
+  }
+
+  CharUnits ElemSize;
+  if (!HandleSizeof(Info, E->getExprLoc(), ElemType, ElemSize))
+    return false;
+  APInt Size, Remainder;
+  APInt ElemSizeAP(ByteSize.getBitWidth(), ElemSize.getQuantity());
+  APInt::udivrem(ByteSize, ElemSizeAP, Size, Remainder);
+  if (Remainder != 0) {
+    // This likely indicates a bug in the implementation of 'std::allocator'.
+    Info.FFDiag(E->getExprLoc(), diag::note_constexpr_operator_new_bad_size)
+        << ByteSize << APSInt(ElemSizeAP, true) << ElemType;
+    return false;
+  }
+
+  if (ByteSize.getActiveBits() > ConstantArrayType::getMaxSizeBits(Info.Ctx)) {
+    if (IsNothrow) {
+      Result.setNull(Info.Ctx, E->getType());
+      return true;
+    }
+
+    Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true);
+    return false;
+  }
+
+  QualType AllocType =
+      Info.Ctx.getConstantArrayType(ElemType, Size, ArrayType::Normal, 0);
+  APValue *Val = Info.createHeapAlloc(E, AllocType, Result);
+  *Val = APValue(APValue::UninitArray(), 0, Size.getZExtValue());
+  Result.addArray(Info, E, cast<ConstantArrayType>(AllocType));
+  return true;
+}
+
+static bool hasVirtualDestructor(QualType T) {
+  if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+    if (CXXDestructorDecl *DD = RD->getDestructor())
+      return DD->isVirtual();
+  return false;
+}
+
+/// Check that the given object is a suitable pointer to a heap allocation that
+/// still exists and is of the right kind for the purpose of a deletion.
+///
+/// On success, returns the heap allocation to deallocate. On failure, produces
+/// a diagnostic and returns None.
+static Optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E,
+                                            const LValue &Pointer,
+                                            DynAlloc::Kind DeallocKind) {
+  auto PointerAsString = [&] {
+    return Pointer.toString(Info.Ctx, Info.Ctx.VoidPtrTy);
+  };
+
+  DynamicAllocLValue DA = Pointer.Base.dyn_cast<DynamicAllocLValue>();
+  if (!DA) {
+    Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc)
+        << PointerAsString();
+    if (Pointer.Base)
+      NoteLValueLocation(Info, Pointer.Base);
+    return None;
+  }
+
+  Optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA);
+  if (!Alloc) {
+    Info.FFDiag(E, diag::note_constexpr_double_delete);
+    return None;
+  }
+
+  QualType AllocType = Pointer.Base.getDynamicAllocType();
+  if (DeallocKind != (*Alloc)->getKind()) {
+    Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
+        << DeallocKind << (*Alloc)->getKind() << AllocType;
+    NoteLValueLocation(Info, Pointer.Base);
+    return None;
+  }
+
+  bool Subobject = false;
+  if (DeallocKind == DynAlloc::New) {
+    Subobject = Pointer.Designator.MostDerivedPathLength != 0 ||
+                Pointer.Designator.isOnePastTheEnd();
+  } else {
+    Subobject = Pointer.Designator.Entries.size() != 1 ||
+                Pointer.Designator.Entries[0].getAsArrayIndex() != 0;
+  }
+  if (Subobject) {
+    Info.FFDiag(E, diag::note_constexpr_delete_subobject)
+        << PointerAsString() << Pointer.Designator.isOnePastTheEnd();
+    return None;
+  }
+
+  return Alloc;
+}
+
+// Perform a call to 'operator delete' or '__builtin_operator_delete'.
+bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) {
+  if (Info.checkingPotentialConstantExpression() ||
+      Info.SpeculativeEvaluationDepth)
+    return false;
+
+  // This is permitted only within a call to std::allocator<T>::deallocate.
+  if (!Info.getStdAllocatorCaller("deallocate")) {
+    Info.FFDiag(E->getExprLoc());
+    return true;
+  }
+
+  LValue Pointer;
+  if (!EvaluatePointer(E->getArg(0), Pointer, Info))
+    return false;
+  for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I)
+    EvaluateIgnoredValue(Info, E->getArg(I));
+
+  if (Pointer.Designator.Invalid)
+    return false;
+
+  // Deleting a null pointer has no effect.
+  if (Pointer.isNullPointer())
+    return true;
+
+  if (!CheckDeleteKind(Info, E, Pointer, DynAlloc::StdAllocator))
+    return false;
+
+  Info.HeapAllocs.erase(Pointer.Base.get<DynamicAllocLValue>());
+  return true;
+}
+
 //===----------------------------------------------------------------------===//
 // Generic Evaluation
 //===----------------------------------------------------------------------===//
@@ -6700,6 +6925,17 @@
           FD = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
         } else
           FD = LambdaCallOp;
+      } else if (FD->isReplaceableGlobalAllocationFunction()) {
+        if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
+            FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) {
+          LValue Ptr;
+          if (!HandleOperatorNewCall(Info, E, Ptr))
+            return false;
+          Ptr.moveInto(Result);
+          return true;
+        } else {
+          return HandleOperatorDeleteCall(Info, E);
+        }
       }
     } else
       return Error(E);
@@ -7565,8 +7801,7 @@
     return true;
   }
   bool ZeroInitialization(const Expr *E) {
-    auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType());
-    Result.setNull(E->getType(), TargetVal);
+    Result.setNull(Info.Ctx, E->getType());
     return true;
   }
 
@@ -7693,12 +7928,22 @@
     // permitted in constant expressions in C++11. Bitcasts from cv void* are
     // also static_casts, but we disallow them as a resolution to DR1312.
     if (!E->getType()->isVoidPointerType()) {
-      Result.Designator.setInvalid();
-      if (SubExpr->getType()->isVoidPointerType())
-        CCEDiag(E, diag::note_constexpr_invalid_cast)
-          << 3 << SubExpr->getType();
-      else
-        CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
+      if (!Result.InvalidBase && !Result.Designator.Invalid &&
+          !Result.IsNullPtr &&
+          Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx),
+                                          E->getType()->getPointeeType()) &&
+          Info.getStdAllocatorCaller("allocate")) {
+        // Inside a call to std::allocator::allocate and friends, we permit
+        // casting from void* back to cv1 T* for a pointer that points to a
+        // cv2 T.
+      } else {
+        Result.Designator.setInvalid();
+        if (SubExpr->getType()->isVoidPointerType())
+          CCEDiag(E, diag::note_constexpr_invalid_cast)
+            << 3 << SubExpr->getType();
+        else
+          CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
+      }
     }
     if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr)
       ZeroInitialization(E);
@@ -7935,6 +8180,8 @@
 
     return true;
   }
+  case Builtin::BI__builtin_operator_new:
+    return HandleOperatorNewCall(Info, E, Result);
   case Builtin::BI__builtin_launder:
     return evaluatePointer(E->getArg(0), Result);
   case Builtin::BIstrchr:
@@ -8186,8 +8433,10 @@
   }
 
   default:
-    return visitNonBuiltinCallExpr(E);
+    break;
   }
+
+  return visitNonBuiltinCallExpr(E);
 }
 
 static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This,
@@ -8203,14 +8452,23 @@
     return false;
 
   FunctionDecl *OperatorNew = E->getOperatorNew();
-  if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+
+  bool IsNothrow = false;
+  bool IsPlacement = false;
+  if (OperatorNew->isReservedGlobalPlacementOperator() &&
+      Info.CurrentCall->isStdFunction() && !E->isArray()) {
+    // FIXME Support array placement new.
+    assert(E->getNumPlacementArgs() == 1);
+    if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
+      return false;
+    if (Result.Designator.Invalid)
+      return false;
+    IsPlacement = true;
+  } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
     Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
         << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
     return false;
-  }
-
-  bool IsNothrow = false;
-  if (E->getNumPlacementArgs()) {
+  } else if (E->getNumPlacementArgs()) {
     // The only new-placement list we support is of the form (std::nothrow).
     //
     // FIXME: There is no restriction on this, but it's not clear that any
@@ -8308,10 +8566,56 @@
            "array allocation with non-array new");
   }
 
-  // Perform the allocation and obtain a pointer to the resulting object.
-  APValue *Val = Info.createHeapAlloc(E, AllocType, Result);
-  if (!Val)
-    return false;
+  APValue *Val;
+  if (IsPlacement) {
+    AccessKinds AK = AK_Construct;
+    struct FindObjectHandler {
+      EvalInfo &Info;
+      const Expr *E;
+      QualType AllocType;
+      const AccessKinds AccessKind;
+      APValue *Value;
+
+      typedef bool result_type;
+      bool failed() { return false; }
+      bool found(APValue &Subobj, QualType SubobjType) {
+        // FIXME: Reject the cases where [basic.life]p8 would not permit the
+        // old name of the object to be used to name the new object.
+        if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) {
+          Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) <<
+            SubobjType << AllocType;
+          return false;
+        }
+        Value = &Subobj;
+        return true;
+      }
+      bool found(APSInt &Value, QualType SubobjType) {
+        Info.FFDiag(E, diag::note_constexpr_construct_complex_elem);
+        return false;
+      }
+      bool found(APFloat &Value, QualType SubobjType) {
+        Info.FFDiag(E, diag::note_constexpr_construct_complex_elem);
+        return false;
+      }
+    } Handler = {Info, E, AllocType, AK, nullptr};
+
+    CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType);
+    if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler))
+      return false;
+
+    Val = Handler.Value;
+
+    // [basic.life]p1:
+    //   The lifetime of an object o of type T ends when [...] the storage
+    //   which the object occupies is [...] reused by an object that is not
+    //   nested within o (6.6.2).
+    *Val = APValue();
+  } else {
+    // Perform the allocation and obtain a pointer to the resulting object.
+    Val = Info.createHeapAlloc(E, AllocType, Result);
+    if (!Val)
+      return false;
+  }
 
   if (ResizedArrayILE) {
     if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE,
@@ -12838,26 +13142,25 @@
 
   bool VisitCallExpr(const CallExpr *E) {
     switch (E->getBuiltinCallee()) {
-    default:
-      return ExprEvaluatorBaseTy::VisitCallExpr(E);
     case Builtin::BI__assume:
     case Builtin::BI__builtin_assume:
       // The argument is not evaluated!
       return true;
+
+    case Builtin::BI__builtin_operator_delete:
+      return HandleOperatorDeleteCall(Info, E);
+
+    default:
+      break;
     }
+
+    return ExprEvaluatorBaseTy::VisitCallExpr(E);
   }
 
   bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);
 };
 } // end anonymous namespace
 
-static bool hasVirtualDestructor(QualType T) {
-  if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
-    if (CXXDestructorDecl *DD = RD->getDestructor())
-      return DD->isVirtual();
-  return false;
-}
-
 bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
   // We cannot speculatively evaluate a delete expression.
   if (Info.SpeculativeEvaluationDepth)
@@ -12888,49 +13191,12 @@
     return true;
   }
 
-  auto PointerAsString = [&] {
-    APValue Printable;
-    Pointer.moveInto(Printable);
-    return Printable.getAsString(Info.Ctx, Arg->getType());
-  };
-
-  DynamicAllocLValue DA = Pointer.Base.dyn_cast<DynamicAllocLValue>();
-  if (!DA) {
-    Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc)
-        << PointerAsString();
-    if (Pointer.Base)
-      NoteLValueLocation(Info, Pointer.Base);
+  Optional<DynAlloc *> Alloc = CheckDeleteKind(
+      Info, E, Pointer, E->isArrayForm() ? DynAlloc::ArrayNew : DynAlloc::New);
+  if (!Alloc)
     return false;
-  }
   QualType AllocType = Pointer.Base.getDynamicAllocType();
 
-  Optional<EvalInfo::DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA);
-  if (!Alloc) {
-    Info.FFDiag(E, diag::note_constexpr_double_delete);
-    return false;
-  }
-
-  if (E->isArrayForm() != AllocType->isConstantArrayType()) {
-    Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
-      << E->isArrayForm() << AllocType;
-    NoteLValueLocation(Info, Pointer.Base);
-    return false;
-  }
-
-  bool Subobject = false;
-  if (E->isArrayForm()) {
-    Subobject = Pointer.Designator.Entries.size() != 1 ||
-                Pointer.Designator.Entries[0].getAsArrayIndex() != 0;
-  } else {
-    Subobject = Pointer.Designator.MostDerivedPathLength != 0 ||
-                Pointer.Designator.isOnePastTheEnd();
-  }
-  if (Subobject) {
-    Info.FFDiag(E, diag::note_constexpr_delete_subobject)
-        << PointerAsString() << Pointer.Designator.isOnePastTheEnd();
-    return false;
-  }
-
   // For the non-array case, the designator must be empty if the static type
   // does not have a virtual destructor.
   if (!E->isArrayForm() && Pointer.Designator.Entries.size() != 0 &&
@@ -12944,7 +13210,7 @@
                          (*Alloc)->Value, AllocType))
     return false;
 
-  if (!Info.HeapAllocs.erase(DA)) {
+  if (!Info.HeapAllocs.erase(Pointer.Base.dyn_cast<DynamicAllocLValue>())) {
     // The element was already erased. This means the destructor call also
     // deleted the object.
     // FIXME: This probably results in undefined behavior before we get this
Index: clang/include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticASTKinds.td
+++ clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -38,8 +38,8 @@
   "pure virtual function %q0 called">;
 def note_constexpr_polymorphic_unknown_dynamic_type : Note<
   "%select{|||||virtual function called on|dynamic_cast applied to|"
-  "typeid applied to|destruction of}0 object '%1' whose dynamic type "
-  "is not constant">;
+  "typeid applied to|construction of|destruction of}0 object '%1' "
+  "whose dynamic type is not constant">;
 def note_constexpr_dynamic_cast_to_reference_failed : Note<
   "reference dynamic_cast failed: %select{"
   "static type %1 of operand is a non-public base class of dynamic type %2|"
@@ -121,11 +121,12 @@
   "evaluation of a call to a 'constexpr' member function">;
 def note_constexpr_lifetime_ended : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
-  "%select{temporary|variable}1 whose lifetime has ended">;
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 %select{temporary|variable}1 whose "
+  "%plural{8:storage duration|:lifetime}0 has ended">;
 def note_constexpr_access_uninit : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|<ERRPR>|destruction of}0 "
   "%select{object outside its lifetime|uninitialized object}1 "
   "is not allowed in a constant expression">;
 def note_constexpr_use_uninit_reference : Note<
@@ -136,18 +137,19 @@
   "in a constant expression">;
 def note_constexpr_access_volatile_type : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "<ERROR>|<ERROR>|<ERROR>}0 "
+  "<ERROR>|<ERROR>|<ERROR>|<ERROR>}0 "
   "volatile-qualified type %1 is not allowed in a constant expression">;
 def note_constexpr_access_volatile_obj : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "<ERROR>|<ERROR>|<ERROR>}0 "
+  "<ERROR>|<ERROR>|<ERROR>|<ERROR>}0 "
   "volatile %select{temporary|object %2|member %2}1 is not allowed in "
   "a constant expression">;
 def note_constexpr_volatile_here : Note<
   "volatile %select{temporary created|object declared|member declared}0 here">;
 def note_constexpr_access_mutable : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 "
   "mutable member %1 is not allowed in a constant expression">;
 def note_constexpr_ltor_non_const_int : Note<
   "read of non-const variable %0 is not allowed in a constant expression">;
@@ -157,35 +159,42 @@
   "read of incomplete type %0 is not allowed in a constant expression">;
 def note_constexpr_access_null : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 "
   "dereferenced null pointer is not allowed in a constant expression">;
 def note_constexpr_access_past_end : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 "
   "dereferenced one-past-the-end pointer is not allowed "
   "in a constant expression">;
 def note_constexpr_access_unsized_array : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 "
   "element of array without known bound "
   "is not allowed in a constant expression">;
 def note_constexpr_access_inactive_union_member : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|"
+  "construction of subobject of|destruction of}0 "
   "member %1 of union with %select{active member %3|no active member}2 "
   "is not allowed in a constant expression">;
 def note_constexpr_access_static_temporary : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 temporary "
+  "member call on|dynamic_cast of|typeid applied to|reconstruction of|"
+  "destruction of}0 temporary "
   "is not allowed in a constant expression outside the expression that "
   "created the temporary">;
 def note_constexpr_access_unreadable_object : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 "
   "object '%1' whose value is not known">;
 def note_constexpr_access_deleted_object : Note<
   "%select{read of|read of|assignment to|increment of|decrement of|"
-  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "member call on|dynamic_cast of|typeid applied to|construction of|"
+  "destruction of}0 "
   "heap allocated object that has been deleted">;
 def note_constexpr_modify_global : Note<
   "a constant expression cannot modify an object that is visible outside "
@@ -255,6 +264,9 @@
 def note_constexpr_pseudo_destructor : Note<
   "pseudo-destructor call is not permitted in constant expressions "
   "until C++20">;
+def note_constexpr_construct_complex_elem : Note<
+  "construction of individual component of complex number is not yet supported "
+  "in constant expressions">;
 def note_constexpr_destroy_complex_elem : Note<
   "destruction of individual component of complex number is not yet supported "
   "in constant expressions">;
@@ -265,6 +277,8 @@
   "call to %select{placement|class-specific}0 %1">;
 def note_constexpr_new_placement : Note<
   "this placement new expression is not yet supported in constant expressions">;
+def note_constexpr_placement_new_wrong_type : Note<
+  "placement new would change type of storage from %0 to %1">;
 def note_constexpr_new_negative : Note<
   "cannot allocate array; evaluated array bound %0 is negative">;
 def note_constexpr_new_too_large : Note<
@@ -272,6 +286,13 @@
 def note_constexpr_new_too_small : Note<
   "cannot allocate array; evaluated array bound %0 is too small to hold "
   "%1 explicitly initialized elements">;
+def note_constexpr_new_untyped : Note<
+  "cannot allocate untyped memory in a constant expression; "
+  "use 'std::allocator<T>::allocate' to allocate memory of type 'T'">;
+def note_constexpr_new_not_complete_object_type : Note<
+  "cannot allocate memory of %select{incomplete|function}0 type %1">;
+def note_constexpr_operator_new_bad_size : Note<
+  "allocated size %0 is not a multiple of size %1 of element type %2">;
 def note_constexpr_delete_not_heap_alloc : Note<
   "delete of pointer '%0' that does not point to a heap-allocated object">;
 def note_constexpr_double_delete : Note<
@@ -279,8 +300,12 @@
 def note_constexpr_double_destroy : Note<
   "destruction of object that is already being destroyed">;
 def note_constexpr_new_delete_mismatch : Note<
-  "%select{non-|}0array delete used to delete pointer to "
-  "%select{|non-}0array object of type %1">;
+  "%plural{2:'delete' used to delete pointer to object "
+  "allocated with 'std::allocator<...>::allocate'|"
+  ":%select{non-array delete|array delete|'std::allocator<...>::deallocate'}0 "
+  "used to delete pointer to "
+  "%select{array object of type %2|non-array object of type %2|"
+  "object allocated with 'new'}0}1">;
 def note_constexpr_delete_subobject : Note<
   "delete of pointer%select{ to subobject|}1 '%0' "
   "%select{|that does not point to complete object}1">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D68364: P... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D683... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D683... Marshall Clow via Phabricator via cfe-commits

Reply via email to