[clang] 1cd5926 - [RFC] Initial implementation of P2719 (#113510)

via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 10 17:13:14 PDT 2025


Author: Oliver Hunt
Date: 2025-04-10T17:13:10-07:00
New Revision: 1cd59264aa2fb4b0ba70ff03c1298b1b5c21271e

URL: https://github.com/llvm/llvm-project/commit/1cd59264aa2fb4b0ba70ff03c1298b1b5c21271e
DIFF: https://github.com/llvm/llvm-project/commit/1cd59264aa2fb4b0ba70ff03c1298b1b5c21271e.diff

LOG: [RFC] Initial implementation of P2719 (#113510)

This is a basic implementation of P2719: "Type-aware allocation and
deallocation functions" described at http://wg21.link/P2719

The proposal includes some more details but the basic change in
functionality is the addition of support for an additional implicit
parameter in operators `new` and `delete` to act as a type tag. Tag is
of type `std::type_identity<T>` where T is the concrete type being
allocated. So for example, a custom type specific allocator for `int`
say can be provided by the declaration of

  void *operator new(std::type_identity<int>, size_t, std::align_val_t);
  void  operator delete(std::type_identity<int>, void*, size_t, std::align_val_t);

However this becomes more powerful by specifying templated declarations,
for example

template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t););

Where the operators being resolved will be the concrete type being
operated over (NB. A completely unconstrained global definition as above
is not recommended as it triggers many problems similar to a general
override of the global operators).

These type aware operators can be declared as either free functions or
in class, and can be specified with or without the other implicit
parameters, with overload resolution performed according to the existing
standard parameter prioritisation, only with type parameterised
operators having higher precedence than non-type aware operators. The
only exception is destroying_delete which for reasons discussed in the
paper we do not support type-aware variants by default.

Added: 
    clang/test/CodeGenCXX/Inputs/std-coroutine.h
    clang/test/CodeGenCXX/type-aware-allocators.cpp
    clang/test/CodeGenCXX/type-aware-coroutines.cpp
    clang/test/CodeGenCXX/type-aware-new-constexpr.cpp
    clang/test/CodeGenCXX/type-aware-placement-operators.cpp
    clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp
    clang/test/SemaCXX/type-aware-coroutines.cpp
    clang/test/SemaCXX/type-aware-new-constexpr.cpp
    clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
    clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
    clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp
    clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
    clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
    clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
    clang/test/SemaCXX/type-aware-placement-operators.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/ASTContext.h
    clang/include/clang/AST/Decl.h
    clang/include/clang/AST/DeclarationName.h
    clang/include/clang/AST/ExprCXX.h
    clang/include/clang/AST/Stmt.h
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Driver/Options.td
    clang/include/clang/Sema/Sema.h
    clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ByteCode/Interp.cpp
    clang/lib/AST/Decl.cpp
    clang/lib/AST/DeclCXX.cpp
    clang/lib/AST/ExprCXX.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/CodeGen/CGExprCXX.cpp
    clang/lib/Frontend/InitPreprocessor.cpp
    clang/lib/Sema/Sema.cpp
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaExprCXX.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.allocation/p1.cpp
    clang/test/CodeGenCoroutines/coro-alloc-2.cpp
    clang/test/Modules/new-delete.cpp
    clang/test/SemaCXX/cxx2a-destroying-delete.cpp
    clang/test/SemaCXX/delete.cpp
    clang/test/SemaCXX/new-delete.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 77bf3355af9da..beab7600e2c32 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -93,6 +93,8 @@ C++2c Feature Support
 
 - Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
 
+- Implemented `P2719R4 Type-aware allocation and deallocation functions <https://wg21.link/P2719>`_.
+
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index c339bb698e099..b8ea2af9215d2 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -329,6 +329,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// This is lazily created.  This is intentionally not serialized.
   mutable llvm::StringMap<StringLiteral *> StringLiteralCache;
 
+  mutable llvm::DenseSet<const FunctionDecl *> DestroyingOperatorDeletes;
+  mutable llvm::DenseSet<const FunctionDecl *> TypeAwareOperatorNewAndDeletes;
+
   /// The next string literal "version" to allocate during constant evaluation.
   /// This is used to distinguish between repeated evaluations of the same
   /// string literal.
@@ -3356,6 +3359,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
   void setStaticLocalNumber(const VarDecl *VD, unsigned Number);
   unsigned getStaticLocalNumber(const VarDecl *VD) const;
 
+  bool hasSeenTypeAwareOperatorNewOrDelete() const {
+    return !TypeAwareOperatorNewAndDeletes.empty();
+  }
+  void setIsDestroyingOperatorDelete(const FunctionDecl *FD, bool IsDestroying);
+  bool isDestroyingOperatorDelete(const FunctionDecl *FD) const;
+  void setIsTypeAwareOperatorNewOrDelete(const FunctionDecl *FD,
+                                         bool IsTypeAware);
+  bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;
+
   /// Retrieve the context for computing mangling numbers in the given
   /// DeclContext.
   MangleNumberingContext &getManglingNumberContext(const DeclContext *DC);

diff  --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 798f112ce7200..3faf63e395a08 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2540,6 +2540,38 @@ class FunctionDecl : public DeclaratorDecl,
   /// If this function is an allocation/deallocation function that takes
   /// the `std::nothrow_t` tag, return true through IsNothrow,
   bool isReplaceableGlobalAllocationFunction(
+      UnsignedOrNone *AlignmentParam = nullptr,
+      bool *IsNothrow = nullptr) const {
+    if (isTypeAwareOperatorNewOrDelete())
+      return false;
+    return isUsableAsGlobalAllocationFunctionInConstantEvaluation(
+        AlignmentParam, IsNothrow);
+  }
+
+  /// Determines whether this function is one of the replaceable global
+  /// allocation functions described in isReplaceableGlobalAllocationFunction,
+  /// or is a function that may be treated as such during constant evaluation.
+  /// This adds support for potentially templated type aware global allocation
+  /// functions of the form:
+  ///    void *operator new(type-identity, std::size_t, std::align_val_t)
+  ///    void *operator new(type-identity, std::size_t, std::align_val_t,
+  ///                       const std::nothrow_t &) noexcept;
+  ///    void *operator new[](type-identity, std::size_t, std::align_val_t)
+  ///    void *operator new[](type-identity, std::size_t, std::align_val_t,
+  ///                         const std::nothrow_t &) noexcept;
+  ///    void operator delete(type-identity, void*, std::size_t,
+  ///                         std::align_val_t) noexcept;
+  ///    void operator delete(type-identity, void*, std::size_t,
+  ///                         std::align_val_t, const std::nothrow_t&) noexcept;
+  ///    void operator delete[](type-identity, void*, std::size_t,
+  ///                         std::align_val_t) noexcept;
+  ///    void operator delete[](type-identity, void*, std::size_t,
+  ///                         std::align_val_t, const std::nothrow_t&) noexcept;
+  /// Where `type-identity` is a specialization of std::type_identity. If the
+  /// declaration is a templated function, it may not include a parameter pack
+  /// in the argument list, the type-identity parameter is required to be
+  /// dependent, and is the only permitted dependent parameter.
+  bool isUsableAsGlobalAllocationFunctionInConstantEvaluation(
       UnsignedOrNone *AlignmentParam = nullptr,
       bool *IsNothrow = nullptr) const;
 
@@ -2548,6 +2580,20 @@ class FunctionDecl : public DeclaratorDecl,
 
   /// Determine whether this is a destroying operator delete.
   bool isDestroyingOperatorDelete() const;
+  void setIsDestroyingOperatorDelete(bool IsDestroyingDelete);
+
+  /// Count of mandatory parameters for type aware operator new
+  static constexpr unsigned RequiredTypeAwareNewParameterCount =
+      /* type-identity */ 1 + /* size */ 1 + /* alignment */ 1;
+
+  /// Count of mandatory parameters for type aware operator delete
+  static constexpr unsigned RequiredTypeAwareDeleteParameterCount =
+      /* type-identity */ 1 + /* address */ 1 + /* size */ 1 +
+      /* alignment */ 1;
+
+  /// Determine whether this is a type aware operator new or delete.
+  bool isTypeAwareOperatorNewOrDelete() const;
+  void setIsTypeAwareOperatorNewOrDelete(bool IsTypeAwareOperator = true);
 
   /// Compute the language linkage.
   LanguageLinkage getLanguageLinkage() const;

diff  --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h
index c9b01dc53964b..9bf740b3bf7ce 100644
--- a/clang/include/clang/AST/DeclarationName.h
+++ b/clang/include/clang/AST/DeclarationName.h
@@ -477,6 +477,34 @@ class DeclarationName {
     return OO_None;
   }
 
+  bool isAnyOperatorNew() const {
+    if (getNameKind() != DeclarationName::CXXOperatorName)
+      return false;
+    switch (getCXXOverloadedOperator()) {
+    case OO_New:
+    case OO_Array_New:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  bool isAnyOperatorDelete() const {
+    if (getNameKind() != DeclarationName::CXXOperatorName)
+      return false;
+    switch (getCXXOverloadedOperator()) {
+    case OO_Delete:
+    case OO_Array_Delete:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  bool isAnyOperatorNewOrDelete() const {
+    return isAnyOperatorNew() || isAnyOperatorDelete();
+  }
+
   /// If this name is the name of a literal operator,
   /// retrieve the identifier associated with it.
   const IdentifierInfo *getCXXLiteralIdentifier() const {

diff  --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index c613ce162a6a4..844f6dd90ae1d 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2235,6 +2235,98 @@ enum class CXXNewInitializationStyle {
   Braces
 };
 
+enum class TypeAwareAllocationMode : unsigned { No, Yes };
+
+inline bool isTypeAwareAllocation(TypeAwareAllocationMode Mode) {
+  return Mode == TypeAwareAllocationMode::Yes;
+}
+
+inline TypeAwareAllocationMode
+typeAwareAllocationModeFromBool(bool IsTypeAwareAllocation) {
+  return IsTypeAwareAllocation ? TypeAwareAllocationMode::Yes
+                               : TypeAwareAllocationMode::No;
+}
+
+enum class AlignedAllocationMode : unsigned { No, Yes };
+
+inline bool isAlignedAllocation(AlignedAllocationMode Mode) {
+  return Mode == AlignedAllocationMode::Yes;
+}
+
+inline AlignedAllocationMode alignedAllocationModeFromBool(bool IsAligned) {
+  return IsAligned ? AlignedAllocationMode::Yes : AlignedAllocationMode::No;
+}
+
+enum class SizedDeallocationMode : unsigned { No, Yes };
+
+inline bool isSizedDeallocation(SizedDeallocationMode Mode) {
+  return Mode == SizedDeallocationMode::Yes;
+}
+
+inline SizedDeallocationMode sizedDeallocationModeFromBool(bool IsSized) {
+  return IsSized ? SizedDeallocationMode::Yes : SizedDeallocationMode::No;
+}
+
+struct ImplicitAllocationParameters {
+  ImplicitAllocationParameters(QualType AllocType,
+                               TypeAwareAllocationMode PassTypeIdentity,
+                               AlignedAllocationMode PassAlignment)
+      : Type(AllocType), PassTypeIdentity(PassTypeIdentity),
+        PassAlignment(PassAlignment) {
+    if (!Type.isNull())
+      Type = Type.getUnqualifiedType();
+  }
+  explicit ImplicitAllocationParameters(AlignedAllocationMode PassAlignment)
+      : PassTypeIdentity(TypeAwareAllocationMode::No),
+        PassAlignment(PassAlignment) {}
+
+  unsigned getNumImplicitArgs() const {
+    unsigned Count = 1; // Size
+    if (isTypeAwareAllocation(PassTypeIdentity))
+      ++Count;
+    if (isAlignedAllocation(PassAlignment))
+      ++Count;
+    return Count;
+  }
+
+  QualType Type;
+  TypeAwareAllocationMode PassTypeIdentity;
+  AlignedAllocationMode PassAlignment;
+};
+
+struct ImplicitDeallocationParameters {
+  ImplicitDeallocationParameters(QualType DeallocType,
+                                 TypeAwareAllocationMode PassTypeIdentity,
+                                 AlignedAllocationMode PassAlignment,
+                                 SizedDeallocationMode PassSize)
+      : Type(DeallocType), PassTypeIdentity(PassTypeIdentity),
+        PassAlignment(PassAlignment), PassSize(PassSize) {
+    if (!Type.isNull())
+      Type = Type.getUnqualifiedType();
+  }
+
+  ImplicitDeallocationParameters(AlignedAllocationMode PassAlignment,
+                                 SizedDeallocationMode PassSize)
+      : PassTypeIdentity(TypeAwareAllocationMode::No),
+        PassAlignment(PassAlignment), PassSize(PassSize) {}
+
+  unsigned getNumImplicitArgs() const {
+    unsigned Count = 1; // Size
+    if (isTypeAwareAllocation(PassTypeIdentity))
+      ++Count;
+    if (isAlignedAllocation(PassAlignment))
+      ++Count;
+    if (isSizedDeallocation(PassSize))
+      ++Count;
+    return Count;
+  }
+
+  QualType Type;
+  TypeAwareAllocationMode PassTypeIdentity;
+  AlignedAllocationMode PassAlignment;
+  SizedDeallocationMode PassSize;
+};
+
 /// Represents a new-expression for memory allocation and constructor
 /// calls, e.g: "new CXXNewExpr(foo)".
 class CXXNewExpr final
@@ -2290,7 +2382,8 @@ class CXXNewExpr final
 
   /// Build a c++ new expression.
   CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
-             FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
+             FunctionDecl *OperatorDelete,
+             const ImplicitAllocationParameters &IAP,
              bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
              SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
              CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2305,7 +2398,7 @@ class CXXNewExpr final
   /// Create a c++ new expression.
   static CXXNewExpr *
   Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew,
-         FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
+         FunctionDecl *OperatorDelete, const ImplicitAllocationParameters &IAP,
          bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
          SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
          CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2394,6 +2487,10 @@ class CXXNewExpr final
     return const_cast<CXXNewExpr *>(this)->getPlacementArg(I);
   }
 
+  unsigned getNumImplicitArgs() const {
+    return implicitAllocationParameters().getNumImplicitArgs();
+  }
+
   bool isParenTypeId() const { return CXXNewExprBits.IsParenTypeId; }
   SourceRange getTypeIdParens() const {
     return isParenTypeId() ? getTrailingObjects<SourceRange>()[0]
@@ -2439,6 +2536,15 @@ class CXXNewExpr final
     return CXXNewExprBits.UsualArrayDeleteWantsSize;
   }
 
+  /// Provides the full set of information about expected implicit
+  /// parameters in this call
+  ImplicitAllocationParameters implicitAllocationParameters() const {
+    return ImplicitAllocationParameters{
+        getAllocatedType(),
+        typeAwareAllocationModeFromBool(CXXNewExprBits.ShouldPassTypeIdentity),
+        alignedAllocationModeFromBool(CXXNewExprBits.ShouldPassAlignment)};
+  }
+
   using arg_iterator = ExprIterator;
   using const_arg_iterator = ConstExprIterator;
 

diff  --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 1ab951a005fd9..336eb6d3df7e1 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -891,6 +891,10 @@ class alignas(void *) Stmt {
     LLVM_PREFERRED_TYPE(bool)
     unsigned ShouldPassAlignment : 1;
 
+    /// Should the type identity be passed to the allocation function?
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned ShouldPassTypeIdentity : 1;
+
     /// If this is an array allocation, does the usual deallocation
     /// function for the allocated type want to know the allocated size?
     LLVM_PREFERRED_TYPE(bool)

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 09f423ad4b4bd..d97bbfee2e4d5 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -64,8 +64,11 @@ def AlwaysInlineCoroutine :
   DiagGroup<"always-inline-coroutine">;
 def CoroNonAlignedAllocationFunction :
   DiagGroup<"coro-non-aligned-allocation-function">;
+def CoroTypeAwareAllocationFunction :
+  DiagGroup<"coro-type-aware-allocation-function">;
 def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine,
-                                        AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction]>;
+                                        AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction,
+                                        CoroTypeAwareAllocationFunction]>;
 def ObjCBoolConstantConversion : DiagGroup<"objc-bool-constant-conversion">;
 def ConstantConversion : DiagGroup<"constant-conversion",
                                    [BitFieldConstantConversion,

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3cb2731488fab..b0972541cb4ab 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2595,8 +2595,8 @@ def err_auto_non_deduced_not_alone : Error<
 def err_implied_std_initializer_list_not_found : Error<
   "cannot deduce type of initializer list because std::initializer_list was "
   "not found; include <initializer_list>">;
-def err_malformed_std_initializer_list : Error<
-  "std::initializer_list must be a class template with a single type parameter">;
+def err_malformed_std_class_template : Error<
+  "std::%0 must be a class template with a single type parameter">;
 def err_auto_init_list_from_c : Error<
   "cannot use %select{'auto'|<ERROR>|'__auto_type'}0 with "
   "%select{initializer list|array}1 in C">;
@@ -9757,7 +9757,7 @@ def err_operator_new_delete_invalid_result_type : Error<
 def err_operator_new_delete_dependent_result_type : Error<
   "%0 cannot have a dependent return type; use %1 instead">;
 def err_operator_new_delete_too_few_parameters : Error<
-  "%0 must have at least one parameter">;
+  "%select{|type aware }0%select{|destroying }1%2 must have at least %select{|one|two|three|four|five}3 parameter%s3">;
 def err_operator_new_delete_template_too_few_parameters : Error<
   "%0 template must have at least two parameters">;
 def warn_operator_new_returns_null : Warning<
@@ -9765,19 +9765,38 @@ def warn_operator_new_returns_null : Warning<
   "%select{| or 'noexcept'}1">, InGroup<OperatorNewReturnsNull>;
 
 def err_operator_new_dependent_param_type : Error<
-  "%0 cannot take a dependent type as first parameter; "
-  "use size_t (%1) instead">;
+  "%select{|type aware }0%select{|destroying }1%2 cannot take a dependent type as its %ordinal3 parameter; "
+  "use %5 (%4) instead">;
 def err_operator_new_param_type : Error<
-  "%0 takes type size_t (%1) as first parameter">;
+  "%select{|type aware }0%select{|destroying }1%2 takes type %5 (%4) as %ordinal3 parameter">;
 def err_operator_new_default_arg: Error<
   "parameter of %0 cannot have a default argument">;
 def err_operator_delete_dependent_param_type : Error<
-  "%0 cannot take a dependent type as first parameter; use %1 instead">;
+  "%select{|type aware }0%select{|destroying }1%2 cannot take a dependent type as its %ordinal3 parameter; "
+  "use %4 instead">;
 def err_operator_delete_param_type : Error<
-  "first parameter of %0 must have type %1">;
+  "%ordinal3 parameter of%select{| type aware}0%select{| destroying}1 %2 must have type %4">;
 def err_destroying_operator_delete_not_usual : Error<
   "destroying operator delete can have only an optional size and optional "
   "alignment parameter">;
+
+def err_type_aware_destroying_operator_delete : Error<
+  "destroying delete is not permitted to be type aware">;
+
+def ext_cxx26_type_aware_allocators : ExtWarn<
+  "type aware allocators are a C++2c extension">, InGroup<CXX26>;
+def warn_cxx26_type_aware_allocators : Warning<
+  "type aware allocators are incompatible with C++ standards before C++2c">,
+  DefaultIgnore, InGroup<CXXPre26Compat>;
+def err_type_aware_allocator_missing_matching_operator : Error<
+  "declaration of type aware %0 in %1 must have matching type aware %2"
+>;
+def note_unmatched_type_aware_allocator_declared : Note<
+  "unmatched type aware %0 declared here">;
+def err_mismatching_type_aware_cleanup_deallocator : Error<
+  "type aware %0 requires a matching type aware %select{|placement }1%2 to be declared in the same scope">;
+def note_type_aware_operator_declared : Note<
+  "%select{non-|}0type aware %1 declared here in %2">;
 def note_implicit_delete_this_in_destructor_here : Note<
   "while checking implicit 'delete this' for virtual destructor">;
 def err_builtin_operator_new_delete_not_usual : Error<
@@ -12149,6 +12168,9 @@ def warn_always_inline_coroutine : Warning<
 def err_coroutine_unusable_new : Error<
   "'operator new' provided by %0 is not usable with the function signature of %1"
 >;
+def note_coroutine_unusable_type_aware_allocators : Note<
+  "type aware %0 will not be used for coroutine allocation"
+>;
 def err_coroutine_unfound_nothrow_new : Error <
   "unable to find %select{'::operator new(size_t, nothrow_t)'|"
   "'::operator new(size_t, align_val_t, nothrow_t)'}1 for %0"
@@ -12168,6 +12190,9 @@ def err_coroutine_return_type : Error<
   "function returns a type %0 marked with [[clang::coro_return_type]] but is neither a coroutine nor a coroutine wrapper; "
   "non-coroutines should be marked with [[clang::coro_wrapper]] to allow returning coroutine return type"
 >;
+def warn_coroutine_type_aware_allocator_ignored : Warning <
+  "type aware %0 will not be used for coroutine allocation">,
+  InGroup<CoroTypeAwareAllocationFunction>;
 } // end of coroutines issue category
 
 let CategoryName = "Documentation Issue" in {

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c42887deebde8..2242269c30b0f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -624,6 +624,7 @@ defvar cpp14 = LangOpts<"CPlusPlus14">;
 defvar cpp17 = LangOpts<"CPlusPlus17">;
 defvar cpp20 = LangOpts<"CPlusPlus20">;
 defvar cpp23 = LangOpts<"CPlusPlus23">;
+defvar cpp26 = LangOpts<"CPlusPlus26">;
 defvar c99 = LangOpts<"C99">;
 defvar c23 = LangOpts<"C23">;
 defvar lang_std = LangOpts<"LangStd">;

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f65dd0191c666..4c70ad38db25f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4891,6 +4891,10 @@ class Sema final : public SemaBase {
   CXXRecordDecl *getStdBadAlloc() const;
   EnumDecl *getStdAlignValT() const;
 
+  TypeAwareAllocationMode ShouldUseTypeAwareOperatorNewOrDelete() const;
+  FunctionDecl *BuildTypeAwareUsualDelete(FunctionTemplateDecl *FnDecl,
+                                          QualType AllocType, SourceLocation);
+
   ValueDecl *tryLookupUnambiguousFieldDecl(RecordDecl *ClassDecl,
                                            const IdentifierInfo *MemberOrBase);
 
@@ -4918,12 +4922,27 @@ class Sema final : public SemaBase {
   /// it is and Element is not NULL, assigns the element type to Element.
   bool isStdInitializerList(QualType Ty, QualType *Element);
 
+  /// Tests whether Ty is an instance of std::type_identity and, if
+  /// it is and TypeArgument is not NULL, assigns the element type to Element.
+  /// If MalformedDecl is not null, and type_identity was ruled out due to being
+  /// incorrectly structured despite having the correct name, the faulty Decl
+  /// will be assigned to MalformedDecl.
+  bool isStdTypeIdentity(QualType Ty, QualType *TypeArgument,
+                         const Decl **MalformedDecl = nullptr);
+
   /// Looks for the std::initializer_list template and instantiates it
   /// with Element, or emits an error if it's not found.
   ///
   /// \returns The instantiated template, or null on error.
   QualType BuildStdInitializerList(QualType Element, SourceLocation Loc);
 
+  /// Looks for the std::type_identity template and instantiates it
+  /// with Type, or returns a null type if type_identity has not been declared
+  ///
+  /// \returns The instantiated template, or null if std::type_identity is not
+  /// declared
+  QualType tryBuildStdTypeIdentity(QualType Type, SourceLocation Loc);
+
   /// Determine whether Ctor is an initializer-list constructor, as
   /// defined in [dcl.init.list]p2.
   bool isInitListConstructor(const FunctionDecl *Ctor);
@@ -6172,6 +6191,10 @@ class Sema final : public SemaBase {
   /// \<initializer_list>.
   ClassTemplateDecl *StdInitializerList;
 
+  /// The C++ "std::type_identity" template, which is defined in
+  /// \<type_traits>.
+  ClassTemplateDecl *StdTypeIdentity;
+
   // Contains the locations of the beginning of unparsed default
   // argument locations.
   llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
@@ -8298,14 +8321,12 @@ class Sema final : public SemaBase {
 
   /// Finds the overloads of operator new and delete that are appropriate
   /// for the allocation.
-  bool FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
-                               AllocationFunctionScope NewScope,
-                               AllocationFunctionScope DeleteScope,
-                               QualType AllocType, bool IsArray,
-                               bool &PassAlignment, MultiExprArg PlaceArgs,
-                               FunctionDecl *&OperatorNew,
-                               FunctionDecl *&OperatorDelete,
-                               bool Diagnose = true);
+  bool FindAllocationFunctions(
+      SourceLocation StartLoc, SourceRange Range,
+      AllocationFunctionScope NewScope, AllocationFunctionScope DeleteScope,
+      QualType AllocType, bool IsArray, ImplicitAllocationParameters &IAP,
+      MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew,
+      FunctionDecl *&OperatorDelete, bool Diagnose = true);
 
   /// DeclareGlobalNewDelete - Declare the global forms of operator new and
   /// delete. These are:
@@ -8336,11 +8357,10 @@ class Sema final : public SemaBase {
 
   bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                 DeclarationName Name, FunctionDecl *&Operator,
-                                bool Diagnose = true, bool WantSize = false,
-                                bool WantAligned = false);
+                                ImplicitDeallocationParameters,
+                                bool Diagnose = true);
   FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
-                                              bool CanProvideSize,
-                                              bool Overaligned,
+                                              ImplicitDeallocationParameters,
                                               DeclarationName Name);
   FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
                                                       CXXRecordDecl *RD,

diff  --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 4552df2a2a31e..f6a43bf5f493b 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -1146,7 +1146,7 @@ class CXXAllocatorCall : public AnyFunctionCall {
   /// C++17 aligned operator new() calls that have alignment implicitly
   /// passed as the second argument, and to 1 for other operator new() calls.
   unsigned getNumImplicitArgs() const {
-    return getOriginExpr()->passAlignment() ? 2 : 1;
+    return getOriginExpr()->getNumImplicitArgs();
   }
 
   unsigned getNumArgs() const override {

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6ad49c27b34d1..b8e6245230475 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13104,6 +13104,32 @@ unsigned ASTContext::getStaticLocalNumber(const VarDecl *VD) const {
   return I != StaticLocalNumbers.end() ? I->second : 1;
 }
 
+void ASTContext::setIsDestroyingOperatorDelete(const FunctionDecl *FD,
+                                               bool IsDestroying) {
+  if (!IsDestroying) {
+    assert(!DestroyingOperatorDeletes.contains(FD->getCanonicalDecl()));
+    return;
+  }
+  DestroyingOperatorDeletes.insert(FD->getCanonicalDecl());
+}
+
+bool ASTContext::isDestroyingOperatorDelete(const FunctionDecl *FD) const {
+  return DestroyingOperatorDeletes.contains(FD->getCanonicalDecl());
+}
+
+void ASTContext::setIsTypeAwareOperatorNewOrDelete(const FunctionDecl *FD,
+                                                   bool IsTypeAware) {
+  if (!IsTypeAware) {
+    assert(!TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl()));
+    return;
+  }
+  TypeAwareOperatorNewAndDeletes.insert(FD->getCanonicalDecl());
+}
+
+bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
+  return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
+}
+
 MangleNumberingContext &
 ASTContext::getManglingNumberContext(const DeclContext *DC) {
   assert(LangOpts.CPlusPlus);  // We don't need mangling numbers for plain C.

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 1528fb9021aec..742ff18031337 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4042,6 +4042,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
   ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
   ToFunction->setFriendConstraintRefersToEnclosingTemplate(
       D->FriendConstraintRefersToEnclosingTemplate());
+  ToFunction->setIsDestroyingOperatorDelete(D->isDestroyingOperatorDelete());
+  ToFunction->setIsTypeAwareOperatorNewOrDelete(
+      D->isTypeAwareOperatorNewOrDelete());
   ToFunction->setRangeEnd(ToEndLoc);
   ToFunction->setDefaultLoc(ToDefaultLoc);
 
@@ -8338,10 +8341,10 @@ ExpectedStmt ASTNodeImporter::VisitCXXNewExpr(CXXNewExpr *E) {
 
   return CXXNewExpr::Create(
       Importer.getToContext(), E->isGlobalNew(), ToOperatorNew,
-      ToOperatorDelete, E->passAlignment(), E->doesUsualArrayDeleteWantSize(),
-      ToPlacementArgs, ToTypeIdParens, ToArraySize, E->getInitializationStyle(),
-      ToInitializer, ToType, ToAllocatedTypeSourceInfo, ToSourceRange,
-      ToDirectInitRange);
+      ToOperatorDelete, E->implicitAllocationParameters(),
+      E->doesUsualArrayDeleteWantSize(), ToPlacementArgs, ToTypeIdParens,
+      ToArraySize, E->getInitializationStyle(), ToInitializer, ToType,
+      ToAllocatedTypeSourceInfo, ToSourceRange, ToDirectInitRange);
 }
 
 ExpectedStmt ASTNodeImporter::VisitCXXDeleteExpr(CXXDeleteExpr *E) {

diff  --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 197c079aaf3e5..eafe735c2dce1 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3380,7 +3380,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
       // Always invalid.
       return this->emitInvalid(E);
     }
-  } else if (!OperatorNew->isReplaceableGlobalAllocationFunction())
+  } else if (!OperatorNew
+                  ->isUsableAsGlobalAllocationFunctionInConstantEvaluation())
     return this->emitInvalidNewDeleteExpr(E, E);
 
   const Descriptor *Desc;
@@ -3600,7 +3601,7 @@ bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
 
   const FunctionDecl *OperatorDelete = E->getOperatorDelete();
 
-  if (!OperatorDelete->isReplaceableGlobalAllocationFunction())
+  if (!OperatorDelete->isUsableAsGlobalAllocationFunctionInConstantEvaluation())
     return this->emitInvalidNewDeleteExpr(E, E);
 
   // Arg must be an lvalue.
@@ -4820,9 +4821,9 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
 
   const FunctionDecl *FuncDecl = E->getDirectCallee();
   // Calls to replaceable operator new/operator delete.
-  if (FuncDecl && FuncDecl->isReplaceableGlobalAllocationFunction()) {
-    if (FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_New ||
-        FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_Array_New) {
+  if (FuncDecl &&
+      FuncDecl->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
+    if (FuncDecl->getDeclName().isAnyOperatorNew()) {
       return VisitBuiltinCallExpr(E, Builtin::BI__builtin_operator_new);
     } else {
       assert(FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_Delete);

diff  --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 4a300c02d007f..768ae6e49b13e 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1205,7 +1205,8 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
       if (const FunctionDecl *VirtualDelete =
               getVirtualOperatorDelete(AllocType);
           VirtualDelete &&
-          !VirtualDelete->isReplaceableGlobalAllocationFunction()) {
+          !VirtualDelete
+               ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
         S.FFDiag(S.Current->getSource(OpPC),
                  diag::note_constexpr_new_non_replaceable)
             << isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
@@ -1723,7 +1724,9 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
         return true;
       S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
           << /*C++26 feature*/ 1 << E->getSourceRange();
-    } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+    } else if (
+        !OperatorNew
+             ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
       S.FFDiag(S.Current->getSource(OpPC),
                diag::note_constexpr_new_non_replaceable)
           << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
@@ -1741,7 +1744,8 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
   } else {
     const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
     const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
-    if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
+    if (!OperatorDelete
+             ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
       S.FFDiag(S.Current->getSource(OpPC),
                diag::note_constexpr_new_non_replaceable)
           << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;

diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 83116ecc0f47b..ad1cb01592e9b 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3082,6 +3082,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
       static_cast<unsigned char>(DeductionCandidate::Normal);
   FunctionDeclBits.HasODRHash = false;
   FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
+
   if (TrailingRequiresClause)
     setTrailingRequiresClause(TrailingRequiresClause);
 }
@@ -3361,17 +3362,15 @@ bool FunctionDecl::isMSVCRTEntryPoint() const {
 }
 
 bool FunctionDecl::isReservedGlobalPlacementOperator() const {
-  if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
-    return false;
-  if (getDeclName().getCXXOverloadedOperator() != OO_New &&
-      getDeclName().getCXXOverloadedOperator() != OO_Delete &&
-      getDeclName().getCXXOverloadedOperator() != OO_Array_New &&
-      getDeclName().getCXXOverloadedOperator() != OO_Array_Delete)
+  if (!getDeclName().isAnyOperatorNewOrDelete())
     return false;
 
   if (!getDeclContext()->getRedeclContext()->isTranslationUnit())
     return false;
 
+  if (isTypeAwareOperatorNewOrDelete())
+    return false;
+
   const auto *proto = getType()->castAs<FunctionProtoType>();
   if (proto->getNumParams() != 2 || proto->isVariadic())
     return false;
@@ -3385,14 +3384,9 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
   return (proto->getParamType(1).getCanonicalType() == Context.VoidPtrTy);
 }
 
-bool FunctionDecl::isReplaceableGlobalAllocationFunction(
+bool FunctionDecl::isUsableAsGlobalAllocationFunctionInConstantEvaluation(
     UnsignedOrNone *AlignmentParam, bool *IsNothrow) const {
-  if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
-    return false;
-  if (getDeclName().getCXXOverloadedOperator() != OO_New &&
-      getDeclName().getCXXOverloadedOperator() != OO_Delete &&
-      getDeclName().getCXXOverloadedOperator() != OO_Array_New &&
-      getDeclName().getCXXOverloadedOperator() != OO_Array_Delete)
+  if (!getDeclName().isAnyOperatorNewOrDelete())
     return false;
 
   if (isa<CXXRecordDecl>(getDeclContext()))
@@ -3402,8 +3396,31 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(
   if (!getDeclContext()->getRedeclContext()->isTranslationUnit())
     return false;
 
+  if (isVariadic())
+    return false;
+
+  if (isTypeAwareOperatorNewOrDelete()) {
+    bool IsDelete = getDeclName().isAnyOperatorDelete();
+    unsigned RequiredParameterCount =
+        IsDelete ? FunctionDecl::RequiredTypeAwareDeleteParameterCount
+                 : FunctionDecl::RequiredTypeAwareNewParameterCount;
+    if (AlignmentParam)
+      *AlignmentParam =
+          /* type identity */ 1U + /* address */ IsDelete + /* size */ 1U;
+    if (RequiredParameterCount == getNumParams())
+      return true;
+    if (getNumParams() > RequiredParameterCount + 1)
+      return false;
+    if (!getParamDecl(RequiredParameterCount)->getType()->isNothrowT())
+      return false;
+
+    if (IsNothrow)
+      *IsNothrow = true;
+    return true;
+  }
+
   const auto *FPT = getType()->castAs<FunctionProtoType>();
-  if (FPT->getNumParams() == 0 || FPT->getNumParams() > 4 || FPT->isVariadic())
+  if (FPT->getNumParams() == 0 || FPT->getNumParams() > 4)
     return false;
 
   // If this is a single-parameter function, it must be a replaceable global
@@ -3423,8 +3440,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(
   // In C++14, the next parameter can be a 'std::size_t' for sized delete.
   bool IsSizedDelete = false;
   if (Ctx.getLangOpts().SizedDeallocation &&
-      (getDeclName().getCXXOverloadedOperator() == OO_Delete ||
-       getDeclName().getCXXOverloadedOperator() == OO_Array_Delete) &&
+      getDeclName().isAnyOperatorDelete() &&
       Ctx.hasSameType(Ty, Ctx.getSizeType())) {
     IsSizedDelete = true;
     Consume();
@@ -3494,17 +3510,19 @@ bool FunctionDecl::isInlineBuiltinDeclaration() const {
 }
 
 bool FunctionDecl::isDestroyingOperatorDelete() const {
-  // C++ P0722:
-  //   Within a class C, a single object deallocation function with signature
-  //     (T, std::destroying_delete_t, <more params>)
-  //   is a destroying operator delete.
-  if (!isa<CXXMethodDecl>(this) || getOverloadedOperator() != OO_Delete ||
-      getNumParams() < 2)
-    return false;
+  return getASTContext().isDestroyingOperatorDelete(this);
+}
+
+void FunctionDecl::setIsDestroyingOperatorDelete(bool IsDestroyingDelete) {
+  getASTContext().setIsDestroyingOperatorDelete(this, IsDestroyingDelete);
+}
+
+bool FunctionDecl::isTypeAwareOperatorNewOrDelete() const {
+  return getASTContext().isTypeAwareOperatorNewOrDelete(this);
+}
 
-  auto *RD = getParamDecl(1)->getType()->getAsCXXRecordDecl();
-  return RD && RD->isInStdNamespace() && RD->getIdentifier() &&
-         RD->getIdentifier()->isStr("destroying_delete_t");
+void FunctionDecl::setIsTypeAwareOperatorNewOrDelete(bool IsTypeAware) {
+  getASTContext().setIsTypeAwareOperatorNewOrDelete(this, IsTypeAware);
 }
 
 LanguageLinkage FunctionDecl::getLanguageLinkage() const {

diff  --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index fffc50eb0b078..d11225285c578 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2530,13 +2530,46 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
 bool CXXMethodDecl::isUsualDeallocationFunction(
     SmallVectorImpl<const FunctionDecl *> &PreventedBy) const {
   assert(PreventedBy.empty() && "PreventedBy is expected to be empty");
-  if (getOverloadedOperator() != OO_Delete &&
-      getOverloadedOperator() != OO_Array_Delete)
+  if (!getDeclName().isAnyOperatorDelete())
     return false;
 
+  if (isTypeAwareOperatorNewOrDelete()) {
+    // A variadic type aware allocation function is not a usual deallocation
+    // function
+    if (isVariadic())
+      return false;
+
+    // Type aware deallocation functions are only usual if they only accept the
+    // mandatory arguments
+    if (getNumParams() != FunctionDecl::RequiredTypeAwareDeleteParameterCount)
+      return false;
+
+    FunctionTemplateDecl *PrimaryTemplate = getPrimaryTemplate();
+    if (!PrimaryTemplate)
+      return true;
+
+    // A template instance is is only a usual deallocation function if it has a
+    // type-identity parameter, the type-identity parameter is a dependent type
+    // (i.e. the type-identity parameter is of type std::type_identity<U> where
+    // U shall be a dependent type), and the type-identity parameter is the only
+    // dependent parameter, and there are no template packs in the parameter
+    // list.
+    FunctionDecl *SpecializedDecl = PrimaryTemplate->getTemplatedDecl();
+    if (!SpecializedDecl->getParamDecl(0)->getType()->isDependentType())
+      return false;
+    for (unsigned Idx = 1; Idx < getNumParams(); ++Idx) {
+      if (SpecializedDecl->getParamDecl(Idx)->getType()->isDependentType())
+        return false;
+    }
+    return true;
+  }
+
   // C++ [basic.stc.dynamic.deallocation]p2:
   //   A template instance is never a usual deallocation function,
   //   regardless of its signature.
+  // Post-P2719 adoption:
+  //   A template instance is is only a usual deallocation function if it has a
+  //   type-identity parameter
   if (getPrimaryTemplate())
     return false;
 

diff  --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index b12f655c4b386..169f11b611066 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -226,7 +226,8 @@ SourceLocation CXXScalarValueInitExpr::getBeginLoc() const {
 
 // CXXNewExpr
 CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
-                       FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
+                       FunctionDecl *OperatorDelete,
+                       const ImplicitAllocationParameters &IAP,
                        bool UsualArrayDeleteWantsSize,
                        ArrayRef<Expr *> PlacementArgs, SourceRange TypeIdParens,
                        std::optional<Expr *> ArraySize,
@@ -245,7 +246,9 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
 
   CXXNewExprBits.IsGlobalNew = IsGlobalNew;
   CXXNewExprBits.IsArray = ArraySize.has_value();
-  CXXNewExprBits.ShouldPassAlignment = ShouldPassAlignment;
+  CXXNewExprBits.ShouldPassAlignment = isAlignedAllocation(IAP.PassAlignment);
+  CXXNewExprBits.ShouldPassTypeIdentity =
+      isTypeAwareAllocation(IAP.PassTypeIdentity);
   CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize;
   CXXNewExprBits.HasInitializer = Initializer != nullptr;
   CXXNewExprBits.StoredInitializationStyle =
@@ -290,7 +293,7 @@ CXXNewExpr::CXXNewExpr(EmptyShell Empty, bool IsArray,
 
 CXXNewExpr *CXXNewExpr::Create(
     const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew,
-    FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
+    FunctionDecl *OperatorDelete, const ImplicitAllocationParameters &IAP,
     bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
     SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
     CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -304,11 +307,10 @@ CXXNewExpr *CXXNewExpr::Create(
       Ctx.Allocate(totalSizeToAlloc<Stmt *, SourceRange>(
                        IsArray + HasInit + NumPlacementArgs, IsParenTypeId),
                    alignof(CXXNewExpr));
-  return new (Mem)
-      CXXNewExpr(IsGlobalNew, OperatorNew, OperatorDelete, ShouldPassAlignment,
-                 UsualArrayDeleteWantsSize, PlacementArgs, TypeIdParens,
-                 ArraySize, InitializationStyle, Initializer, Ty,
-                 AllocatedTypeInfo, Range, DirectInitRange);
+  return new (Mem) CXXNewExpr(
+      IsGlobalNew, OperatorNew, OperatorDelete, IAP, UsualArrayDeleteWantsSize,
+      PlacementArgs, TypeIdParens, ArraySize, InitializationStyle, Initializer,
+      Ty, AllocatedTypeInfo, Range, DirectInitRange);
 }
 
 CXXNewExpr *CXXNewExpr::CreateEmpty(const ASTContext &Ctx, bool IsArray,

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 80ece3c4ed7e1..d1cc722fb7945 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8385,9 +8385,8 @@ class ExprEvaluatorBase
           FD = CorrespondingCallOpSpecialization;
         } else
           FD = LambdaCallOp;
-      } else if (FD->isReplaceableGlobalAllocationFunction()) {
-        if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
-            FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) {
+      } else if (FD->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
+        if (FD->getDeclName().isAnyOperatorNew()) {
           LValue Ptr;
           if (!HandleOperatorNewCall(Info, E, Ptr))
             return false;
@@ -10319,7 +10318,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     Info.FFDiag(E, diag::note_constexpr_new_placement)
         << /*Unsupported*/ 0 << E->getSourceRange();
     return false;
-  } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+  } else if (!OperatorNew
+                  ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
     Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
         << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
     return false;
@@ -16524,7 +16524,8 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
     return false;
 
   FunctionDecl *OperatorDelete = E->getOperatorDelete();
-  if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
+  if (!OperatorDelete
+           ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
     Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
         << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
     return false;
@@ -16568,7 +16569,8 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
   if (!E->isArrayForm() && !E->isGlobalDelete()) {
     const FunctionDecl *VirtualDelete = getVirtualOperatorDelete(AllocType);
     if (VirtualDelete &&
-        !VirtualDelete->isReplaceableGlobalAllocationFunction()) {
+        !VirtualDelete
+             ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
       Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
           << isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
       return false;

diff  --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 5c11c0bceade7..a0d21a4de469f 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1382,9 +1382,10 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
 namespace {
 /// The parameters to pass to a usual operator delete.
 struct UsualDeleteParams {
+  TypeAwareAllocationMode TypeAwareDelete = TypeAwareAllocationMode::No;
   bool DestroyingDelete = false;
   bool Size = false;
-  bool Alignment = false;
+  AlignedAllocationMode Alignment = AlignedAllocationMode::No;
 };
 }
 
@@ -1394,11 +1395,20 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
   const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>();
   auto AI = FPT->param_type_begin(), AE = FPT->param_type_end();
 
-  // The first argument is always a void*.
+  if (FD->isTypeAwareOperatorNewOrDelete()) {
+    Params.TypeAwareDelete = TypeAwareAllocationMode::Yes;
+    assert(AI != AE);
+    ++AI;
+  }
+
+  // The first argument after the type-identity parameter (if any) is
+  // always a void* (or C* for a destroying operator delete for class
+  // type C).
   ++AI;
 
   // The next parameter may be a std::destroying_delete_t.
   if (FD->isDestroyingOperatorDelete()) {
+    assert(!isTypeAwareAllocation(Params.TypeAwareDelete));
     Params.DestroyingDelete = true;
     assert(AI != AE);
     ++AI;
@@ -1408,12 +1418,14 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
   if (AI != AE && (*AI)->isIntegerType()) {
     Params.Size = true;
     ++AI;
-  }
+  } else
+    assert(!isTypeAwareAllocation(Params.TypeAwareDelete));
 
   if (AI != AE && (*AI)->isAlignValT()) {
-    Params.Alignment = true;
+    Params.Alignment = AlignedAllocationMode::Yes;
     ++AI;
-  }
+  } else
+    assert(!isTypeAwareAllocation(Params.TypeAwareDelete));
 
   assert(AI == AE && "unexpected usual deallocation function parameter");
   return Params;
@@ -1434,10 +1446,13 @@ namespace {
       QualType ArgType;
     };
 
-    unsigned NumPlacementArgs : 31;
-    LLVM_PREFERRED_TYPE(bool)
+    unsigned NumPlacementArgs : 30;
+    LLVM_PREFERRED_TYPE(AlignedAllocationMode)
     unsigned PassAlignmentToPlacementDelete : 1;
+    LLVM_PREFERRED_TYPE(TypeAwareAllocationMode)
+    unsigned PassTypeToPlacementDelete : 1;
     const FunctionDecl *OperatorDelete;
+    RValueTy TypeIdentity;
     ValueTy Ptr;
     ValueTy AllocSize;
     CharUnits AllocAlign;
@@ -1452,13 +1467,15 @@ namespace {
     }
 
     CallDeleteDuringNew(size_t NumPlacementArgs,
-                        const FunctionDecl *OperatorDelete, ValueTy Ptr,
-                        ValueTy AllocSize, bool PassAlignmentToPlacementDelete,
+                        const FunctionDecl *OperatorDelete,
+                        RValueTy TypeIdentity, ValueTy Ptr, ValueTy AllocSize,
+                        const ImplicitAllocationParameters &IAP,
                         CharUnits AllocAlign)
-      : NumPlacementArgs(NumPlacementArgs),
-        PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete),
-        OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
-        AllocAlign(AllocAlign) {}
+        : NumPlacementArgs(NumPlacementArgs),
+          PassAlignmentToPlacementDelete(
+              isAlignedAllocation(IAP.PassAlignment)),
+          OperatorDelete(OperatorDelete), TypeIdentity(TypeIdentity), Ptr(Ptr),
+          AllocSize(AllocSize), AllocAlign(AllocAlign) {}
 
     void setPlacementArg(unsigned I, RValueTy Arg, QualType Type) {
       assert(I < NumPlacementArgs && "index out of range");
@@ -1468,17 +1485,28 @@ namespace {
     void Emit(CodeGenFunction &CGF, Flags flags) override {
       const auto *FPT = OperatorDelete->getType()->castAs<FunctionProtoType>();
       CallArgList DeleteArgs;
-
-      // The first argument is always a void* (or C* for a destroying operator
-      // delete for class type C).
-      DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0));
+      unsigned FirstNonTypeArg = 0;
+      TypeAwareAllocationMode TypeAwareDeallocation =
+          TypeAwareAllocationMode::No;
+      if (OperatorDelete->isTypeAwareOperatorNewOrDelete()) {
+        TypeAwareDeallocation = TypeAwareAllocationMode::Yes;
+        QualType SpecializedTypeIdentity = FPT->getParamType(0);
+        ++FirstNonTypeArg;
+        DeleteArgs.add(Traits::get(CGF, TypeIdentity), SpecializedTypeIdentity);
+      }
+      // The first argument after type-identity parameter (if any) is always
+      // a void* (or C* for a destroying operator delete for class type C).
+      DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(FirstNonTypeArg));
 
       // Figure out what other parameters we should be implicitly passing.
       UsualDeleteParams Params;
       if (NumPlacementArgs) {
         // A placement deallocation function is implicitly passed an alignment
         // if the placement allocation function was, but is never passed a size.
-        Params.Alignment = PassAlignmentToPlacementDelete;
+        Params.Alignment =
+            alignedAllocationModeFromBool(PassAlignmentToPlacementDelete);
+        Params.TypeAwareDelete = TypeAwareDeallocation;
+        Params.Size = isTypeAwareAllocation(Params.TypeAwareDelete);
       } else {
         // For a non-placement new-expression, 'operator delete' can take a
         // size and/or an alignment if it has the right parameters.
@@ -1497,7 +1525,7 @@ namespace {
       // is an enum whose underlying type is std::size_t.
       // FIXME: Use the right type as the parameter type. Note that in a call
       // to operator delete(size_t, ...), we may not have it available.
-      if (Params.Alignment)
+      if (isAlignedAllocation(Params.Alignment))
         DeleteArgs.add(RValue::get(llvm::ConstantInt::get(
                            CGF.SizeTy, AllocAlign.getQuantity())),
                        CGF.getContext().getSizeType());
@@ -1516,13 +1544,11 @@ namespace {
 
 /// Enter a cleanup to call 'operator delete' if the initializer in a
 /// new-expression throws.
-static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
-                                  const CXXNewExpr *E,
-                                  Address NewPtr,
-                                  llvm::Value *AllocSize,
-                                  CharUnits AllocAlign,
+static void EnterNewDeleteCleanup(CodeGenFunction &CGF, const CXXNewExpr *E,
+                                  RValue TypeIdentity, Address NewPtr,
+                                  llvm::Value *AllocSize, CharUnits AllocAlign,
                                   const CallArgList &NewArgs) {
-  unsigned NumNonPlacementArgs = E->passAlignment() ? 2 : 1;
+  unsigned NumNonPlacementArgs = E->getNumImplicitArgs();
 
   // If we're not inside a conditional branch, then the cleanup will
   // dominate and we can do the easier (and more efficient) thing.
@@ -1538,7 +1564,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
 
     DirectCleanup *Cleanup = CGF.EHStack.pushCleanupWithExtra<DirectCleanup>(
         EHCleanup, E->getNumPlacementArgs(), E->getOperatorDelete(),
-        NewPtr.emitRawPointer(CGF), AllocSize, E->passAlignment(), AllocAlign);
+        TypeIdentity, NewPtr.emitRawPointer(CGF), AllocSize,
+        E->implicitAllocationParameters(), AllocAlign);
     for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
       auto &Arg = NewArgs[I + NumNonPlacementArgs];
       Cleanup->setPlacementArg(I, Arg.getRValue(CGF), Arg.Ty);
@@ -1552,7 +1579,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
       DominatingValue<RValue>::save(CGF, RValue::get(NewPtr, CGF));
   DominatingValue<RValue>::saved_type SavedAllocSize =
     DominatingValue<RValue>::save(CGF, RValue::get(AllocSize));
-
+  DominatingValue<RValue>::saved_type SavedTypeIdentity =
+      DominatingValue<RValue>::save(CGF, TypeIdentity);
   struct ConditionalCleanupTraits {
     typedef DominatingValue<RValue>::saved_type ValueTy;
     typedef DominatingValue<RValue>::saved_type RValueTy;
@@ -1562,14 +1590,11 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
   };
   typedef CallDeleteDuringNew<ConditionalCleanupTraits> ConditionalCleanup;
 
-  ConditionalCleanup *Cleanup = CGF.EHStack
-    .pushCleanupWithExtra<ConditionalCleanup>(EHCleanup,
-                                              E->getNumPlacementArgs(),
-                                              E->getOperatorDelete(),
-                                              SavedNewPtr,
-                                              SavedAllocSize,
-                                              E->passAlignment(),
-                                              AllocAlign);
+  ConditionalCleanup *Cleanup =
+      CGF.EHStack.pushCleanupWithExtra<ConditionalCleanup>(
+          EHCleanup, E->getNumPlacementArgs(), E->getOperatorDelete(),
+          SavedTypeIdentity, SavedNewPtr, SavedAllocSize,
+          E->implicitAllocationParameters(), AllocAlign);
   for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
     auto &Arg = NewArgs[I + NumNonPlacementArgs];
     Cleanup->setPlacementArg(
@@ -1589,6 +1614,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   // If there is a brace-initializer or C++20 parenthesized initializer, cannot
   // allocate fewer elements than inits.
   unsigned minElements = 0;
+  unsigned IndexOfAlignArg = 1;
   if (E->isArray() && E->hasInitializer()) {
     const Expr *Init = E->getInitializer();
     const InitListExpr *ILE = dyn_cast<InitListExpr>(Init);
@@ -1615,6 +1641,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   // operator, just "inline" it directly.
   Address allocation = Address::invalid();
   CallArgList allocatorArgs;
+  RValue TypeIdentityArg;
   if (allocator->isReservedGlobalPlacementOperator()) {
     assert(E->getNumPlacementArgs() == 1);
     const Expr *arg = *E->placement_arguments().begin();
@@ -1639,8 +1666,17 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   } else {
     const FunctionProtoType *allocatorType =
       allocator->getType()->castAs<FunctionProtoType>();
+    ImplicitAllocationParameters IAP = E->implicitAllocationParameters();
     unsigned ParamsToSkip = 0;
-
+    if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+      QualType SpecializedTypeIdentity = allocatorType->getParamType(0);
+      CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
+                                               SourceLocation());
+      TypeIdentityArg = EmitAnyExprToTemp(&TypeIdentityParam);
+      allocatorArgs.add(TypeIdentityArg, SpecializedTypeIdentity);
+      ++ParamsToSkip;
+      ++IndexOfAlignArg;
+    }
     // The allocation size is the first argument.
     QualType sizeType = getContext().getSizeType();
     allocatorArgs.add(RValue::get(allocSize), sizeType);
@@ -1652,10 +1688,10 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
     }
 
     // The allocation alignment may be passed as the second argument.
-    if (E->passAlignment()) {
+    if (isAlignedAllocation(IAP.PassAlignment)) {
       QualType AlignValT = sizeType;
-      if (allocatorType->getNumParams() > 1) {
-        AlignValT = allocatorType->getParamType(1);
+      if (allocatorType->getNumParams() > IndexOfAlignArg) {
+        AlignValT = allocatorType->getParamType(IndexOfAlignArg);
         assert(getContext().hasSameUnqualifiedType(
                    AlignValT->castAs<EnumType>()->getDecl()->getIntegerType(),
                    sizeType) &&
@@ -1732,8 +1768,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   llvm::Instruction *cleanupDominator = nullptr;
   if (E->getOperatorDelete() &&
       !E->getOperatorDelete()->isReservedGlobalPlacementOperator()) {
-    EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocAlign,
-                          allocatorArgs);
+    EnterNewDeleteCleanup(*this, E, TypeIdentityArg, allocation, allocSize,
+                          allocAlign, allocatorArgs);
     operatorDeleteCleanup = EHStack.stable_begin();
     cleanupDominator = Builder.CreateUnreachable();
   }
@@ -1811,21 +1847,29 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   auto Params = getUsualDeleteParams(DeleteFD);
   auto ParamTypeIt = DeleteFTy->param_type_begin();
 
+  std::optional<llvm::AllocaInst *> TagAlloca;
+  auto EmitTag = [&](QualType TagType, const char *TagName) {
+    assert(!TagAlloca);
+    llvm::Type *Ty = getTypes().ConvertType(TagType);
+    CharUnits Align = CGM.getNaturalTypeAlignment(TagType);
+    llvm::AllocaInst *TagAllocation = CreateTempAlloca(Ty, TagName);
+    TagAllocation->setAlignment(Align.getAsAlign());
+    DeleteArgs.add(RValue::getAggregate(Address(TagAllocation, Ty, Align)),
+                   TagType);
+    TagAlloca = TagAllocation;
+  };
+
+  // Pass std::type_identity tag if present
+  if (isTypeAwareAllocation(Params.TypeAwareDelete))
+    EmitTag(*ParamTypeIt++, "typeaware.delete.tag");
+
   // Pass the pointer itself.
   QualType ArgTy = *ParamTypeIt++;
   DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
 
   // Pass the std::destroying_delete tag if present.
-  llvm::AllocaInst *DestroyingDeleteTag = nullptr;
-  if (Params.DestroyingDelete) {
-    QualType DDTag = *ParamTypeIt++;
-    llvm::Type *Ty = getTypes().ConvertType(DDTag);
-    CharUnits Align = CGM.getNaturalTypeAlignment(DDTag);
-    DestroyingDeleteTag = CreateTempAlloca(Ty, "destroying.delete.tag");
-    DestroyingDeleteTag->setAlignment(Align.getAsAlign());
-    DeleteArgs.add(
-        RValue::getAggregate(Address(DestroyingDeleteTag, Ty, Align)), DDTag);
-  }
+  if (Params.DestroyingDelete)
+    EmitTag(*ParamTypeIt++, "destroying.delete.tag");
 
   // Pass the size if the delete function has a size_t parameter.
   if (Params.Size) {
@@ -1847,7 +1891,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   }
 
   // Pass the alignment if the delete function has an align_val_t parameter.
-  if (Params.Alignment) {
+  if (isAlignedAllocation(Params.Alignment)) {
     QualType AlignValType = *ParamTypeIt++;
     CharUnits DeleteTypeAlign =
         getContext().toCharUnitsFromBits(getContext().getTypeAlignIfKnown(
@@ -1863,12 +1907,11 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   // Emit the call to delete.
   EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs);
 
-  // If call argument lowering didn't use the destroying_delete_t alloca,
-  // remove it again.
-  if (DestroyingDeleteTag && DestroyingDeleteTag->use_empty())
-    DestroyingDeleteTag->eraseFromParent();
+  // If call argument lowering didn't use a generated tag argument alloca we
+  // remove them
+  if (TagAlloca && (*TagAlloca)->use_empty())
+    (*TagAlloca)->eraseFromParent();
 }
-
 namespace {
   /// Calls the given 'operator delete' on a single object.
   struct CallObjectDelete final : EHScopeStack::Cleanup {

diff  --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 90d0f5c8c9d1a..659af42478991 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -776,6 +776,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
+
+  // TODO: Final number?
+  Builder.defineMacro("__cpp_type_aware_allocators", "202500L");
 }
 
 /// InitializeOpenCLFeatureTestMacros - Define OpenCL macros based on target

diff  --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 32d7744be9229..d2da9cd1201c2 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -258,6 +258,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr),
       StdCoroutineTraitsCache(nullptr), IdResolver(pp),
       OriginalLexicalContext(nullptr), StdInitializerList(nullptr),
+      StdTypeIdentity(nullptr),
       FullyCheckedComparisonCategories(
           static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
       StdSourceLocationImplDecl(nullptr), CXXTypeInfoDecl(nullptr),

diff  --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 6f873cafa98fd..d631ad11fc9f5 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1097,12 +1097,39 @@ static TypeSourceInfo *getTypeSourceInfoForStdAlignValT(Sema &S,
   return S.Context.getTrivialTypeSourceInfo(StdAlignValDecl);
 }
 
+// When searching for custom allocators on the PromiseType we want to
+// warn that we will ignore type aware allocators.
+static bool DiagnoseTypeAwareAllocators(Sema &S, SourceLocation Loc,
+                                        unsigned DiagnosticID,
+                                        DeclarationName Name,
+                                        QualType PromiseType) {
+  assert(PromiseType->isRecordType());
+
+  LookupResult R(S, Name, Loc, Sema::LookupOrdinaryName);
+  S.LookupQualifiedName(R, PromiseType->getAsCXXRecordDecl());
+  bool HaveIssuedWarning = false;
+  for (auto Decl : R) {
+    if (!Decl->getAsFunction()->isTypeAwareOperatorNewOrDelete())
+      continue;
+    if (!HaveIssuedWarning) {
+      S.Diag(Loc, DiagnosticID) << Name;
+      HaveIssuedWarning = true;
+    }
+    S.Diag(Decl->getLocation(), diag::note_type_aware_operator_declared)
+        << /* isTypeAware=*/1 << Decl << Decl->getDeclContext();
+  }
+  R.suppressDiagnostics();
+  return HaveIssuedWarning;
+}
+
 // Find an appropriate delete for the promise.
 static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseType,
                                  FunctionDecl *&OperatorDelete) {
   DeclarationName DeleteName =
       S.Context.DeclarationNames.getCXXOperatorName(OO_Delete);
-
+  DiagnoseTypeAwareAllocators(S, Loc,
+                              diag::warn_coroutine_type_aware_allocator_ignored,
+                              DeleteName, PromiseType);
   auto *PointeeRD = PromiseType->getAsCXXRecordDecl();
   assert(PointeeRD && "PromiseType must be a CxxRecordDecl type");
 
@@ -1112,9 +1139,10 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
   // The deallocation function's name is looked up by searching for it in the
   // scope of the promise type. If nothing is found, a search is performed in
   // the global scope.
+  ImplicitDeallocationParameters IDP = {
+      alignedAllocationModeFromBool(Overaligned), SizedDeallocationMode::Yes};
   if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
-                                 /*Diagnose*/ true, /*WantSize*/ true,
-                                 /*WantAligned*/ Overaligned))
+                                 IDP, /*Diagnose=*/true))
     return false;
 
   // [dcl.fct.def.coroutine]p12
@@ -1125,18 +1153,18 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
   //   shall be the function with one parameter.
   if (!OperatorDelete) {
     // Look for a global declaration.
-    // Coroutines can always provide their required size.
-    const bool CanProvideSize = true;
     // Sema::FindUsualDeallocationFunction will try to find the one with two
     // parameters first. It will return the deallocation function with one
     // parameter if failed.
-    OperatorDelete = S.FindUsualDeallocationFunction(Loc, CanProvideSize,
-                                                     Overaligned, DeleteName);
+    // Coroutines can always provide their required size.
+    IDP.PassSize = SizedDeallocationMode::Yes;
+    OperatorDelete = S.FindUsualDeallocationFunction(Loc, IDP, DeleteName);
 
     if (!OperatorDelete)
       return false;
   }
 
+  assert(!OperatorDelete->isTypeAwareOperatorNewOrDelete());
   S.MarkFunctionReferenced(Loc, OperatorDelete);
   return true;
 }
@@ -1411,10 +1439,10 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
 
   FunctionDecl *OperatorNew = nullptr;
   SmallVector<Expr *, 1> PlacementArgs;
+  DeclarationName NewName =
+      S.getASTContext().DeclarationNames.getCXXOperatorName(OO_New);
 
-  const bool PromiseContainsNew = [this, &PromiseType]() -> bool {
-    DeclarationName NewName =
-        S.getASTContext().DeclarationNames.getCXXOperatorName(OO_New);
+  const bool PromiseContainsNew = [this, &PromiseType, NewName]() -> bool {
     LookupResult R(S, NewName, Loc, Sema::LookupOrdinaryName);
 
     if (PromiseType->isRecordType())
@@ -1425,7 +1453,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
 
   // Helper function to indicate whether the last lookup found the aligned
   // allocation function.
-  bool PassAlignment = S.getLangOpts().CoroAlignedAllocation;
+  ImplicitAllocationParameters IAP(
+      alignedAllocationModeFromBool(S.getLangOpts().CoroAlignedAllocation));
   auto LookupAllocationFunction = [&](Sema::AllocationFunctionScope NewScope =
                                           Sema::AFS_Both,
                                       bool WithoutPlacementArgs = false,
@@ -1439,14 +1468,19 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     if (NewScope == Sema::AFS_Both)
       NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
 
-    PassAlignment = !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
+    bool ShouldUseAlignedAlloc =
+        !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
+    IAP = ImplicitAllocationParameters(
+        alignedAllocationModeFromBool(ShouldUseAlignedAlloc));
+
     FunctionDecl *UnusedResult = nullptr;
     S.FindAllocationFunctions(Loc, SourceRange(), NewScope,
-                              /*DeleteScope*/ Sema::AFS_Both, PromiseType,
-                              /*isArray*/ false, PassAlignment,
+                              /*DeleteScope=*/Sema::AFS_Both, PromiseType,
+                              /*isArray=*/false, IAP,
                               WithoutPlacementArgs ? MultiExprArg{}
                                                    : PlacementArgs,
-                              OperatorNew, UnusedResult, /*Diagnose*/ false);
+                              OperatorNew, UnusedResult, /*Diagnose=*/false);
+    assert(!OperatorNew || !OperatorNew->isTypeAwareOperatorNewOrDelete());
   };
 
   // We don't expect to call to global operator new with (size, p0, …, pn).
@@ -1470,8 +1504,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     // by passing the amount of space requested as an argument of type
     // std::size_t as the first argument, and the requested alignment as
     // an argument of type std:align_val_t as the second argument.
-    if (!OperatorNew ||
-        (S.getLangOpts().CoroAlignedAllocation && !PassAlignment))
+    if (!OperatorNew || (S.getLangOpts().CoroAlignedAllocation &&
+                         !isAlignedAllocation(IAP.PassAlignment)))
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
                                /*WithoutPlacementArgs*/ true);
   }
@@ -1496,7 +1530,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper variable to emit warnings.
   bool FoundNonAlignedInPromise = false;
   if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
-    if (!OperatorNew || !PassAlignment) {
+    if (!OperatorNew || !isAlignedAllocation(IAP.PassAlignment)) {
       FoundNonAlignedInPromise = OperatorNew;
 
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
@@ -1533,14 +1567,22 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   }
 
   if (!OperatorNew) {
-    if (PromiseContainsNew)
+    if (PromiseContainsNew) {
       S.Diag(Loc, diag::err_coroutine_unusable_new) << PromiseType << &FD;
-    else if (RequiresNoThrowAlloc)
+      DiagnoseTypeAwareAllocators(
+          S, Loc, diag::note_coroutine_unusable_type_aware_allocators, NewName,
+          PromiseType);
+    } else if (RequiresNoThrowAlloc)
       S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new)
           << &FD << S.getLangOpts().CoroAlignedAllocation;
 
     return false;
   }
+  assert(!OperatorNew->isTypeAwareOperatorNewOrDelete());
+
+  DiagnoseTypeAwareAllocators(S, Loc,
+                              diag::warn_coroutine_type_aware_allocator_ignored,
+                              NewName, PromiseType);
 
   if (RequiresNoThrowAlloc) {
     const auto *FT = OperatorNew->getType()->castAs<FunctionProtoType>();
@@ -1562,6 +1604,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     return false;
   }
 
+  assert(!OperatorDelete->isTypeAwareOperatorNewOrDelete());
+
   Expr *FramePtr =
       S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_frame, {});
 
@@ -1591,7 +1635,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     return false;
 
   SmallVector<Expr *, 2> NewArgs(1, FrameSize);
-  if (S.getLangOpts().CoroAlignedAllocation && PassAlignment)
+  if (S.getLangOpts().CoroAlignedAllocation &&
+      isAlignedAllocation(IAP.PassAlignment))
     NewArgs.push_back(FrameAlignment);
 
   if (OperatorNew->getNumParams() > NewArgs.size())

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 08bb74190a767..e9805c345b6af 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10169,10 +10169,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       // deallocation function shall not be declared with the consteval
       // specifier.
       if (ConstexprKind == ConstexprSpecKind::Consteval &&
-          (NewFD->getOverloadedOperator() == OO_New ||
-           NewFD->getOverloadedOperator() == OO_Array_New ||
-           NewFD->getOverloadedOperator() == OO_Delete ||
-           NewFD->getOverloadedOperator() == OO_Array_Delete)) {
+          NewFD->getDeclName().isAnyOperatorNewOrDelete()) {
         Diag(D.getDeclSpec().getConstexprSpecLoc(),
              diag::err_invalid_consteval_decl_kind)
             << NewFD;
@@ -10276,9 +10273,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
     //   A deallocation function with no exception-specification is treated
     //   as if it were specified with noexcept(true).
     const FunctionProtoType *FPT = R->getAs<FunctionProtoType>();
-    if ((Name.getCXXOverloadedOperator() == OO_Delete ||
-         Name.getCXXOverloadedOperator() == OO_Array_Delete) &&
-        getLangOpts().CPlusPlus11 && FPT && !FPT->hasExceptionSpec())
+    if (Name.isAnyOperatorDelete() && getLangOpts().CPlusPlus11 && FPT &&
+        !FPT->hasExceptionSpec())
       NewFD->setType(Context.getFunctionType(
           FPT->getReturnType(), FPT->getParamTypes(),
           FPT->getExtProtoInfo().withExceptionSpec(EST_BasicNoexcept)));

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 721f61ca409ab..e4f09ca60d7d8 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -46,6 +46,7 @@
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
 #include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLForwardCompat.h"
@@ -7322,6 +7323,48 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
     else if (Record->hasAttr<CUDADeviceBuiltinTextureTypeAttr>())
       checkCUDADeviceBuiltinTextureClassTemplate(*this, Record);
   }
+
+  llvm::SmallDenseMap<OverloadedOperatorKind,
+                      llvm::SmallVector<const FunctionDecl *, 2>, 4>
+      TypeAwareDecls{{OO_New, {}},
+                     {OO_Array_New, {}},
+                     {OO_Delete, {}},
+                     {OO_Array_New, {}}};
+  for (auto *D : Record->decls()) {
+    const FunctionDecl *FnDecl = D->getAsFunction();
+    if (!FnDecl || !FnDecl->isTypeAwareOperatorNewOrDelete())
+      continue;
+    assert(FnDecl->getDeclName().isAnyOperatorNewOrDelete());
+    TypeAwareDecls[FnDecl->getOverloadedOperator()].push_back(FnDecl);
+  }
+  auto CheckMismatchedTypeAwareAllocators =
+      [this, &TypeAwareDecls, Record](OverloadedOperatorKind NewKind,
+                                      OverloadedOperatorKind DeleteKind) {
+        auto &NewDecls = TypeAwareDecls[NewKind];
+        auto &DeleteDecls = TypeAwareDecls[DeleteKind];
+        if (NewDecls.empty() == DeleteDecls.empty())
+          return;
+        DeclarationName FoundOperator =
+            Context.DeclarationNames.getCXXOperatorName(
+                NewDecls.empty() ? DeleteKind : NewKind);
+        DeclarationName MissingOperator =
+            Context.DeclarationNames.getCXXOperatorName(
+                NewDecls.empty() ? NewKind : DeleteKind);
+        Diag(Record->getLocation(),
+             diag::err_type_aware_allocator_missing_matching_operator)
+            << FoundOperator << Context.getRecordType(Record)
+            << MissingOperator;
+        for (auto MD : NewDecls)
+          Diag(MD->getLocation(),
+               diag::note_unmatched_type_aware_allocator_declared)
+              << MD;
+        for (auto MD : DeleteDecls)
+          Diag(MD->getLocation(),
+               diag::note_unmatched_type_aware_allocator_declared)
+              << MD;
+      };
+  CheckMismatchedTypeAwareAllocators(OO_New, OO_Delete);
+  CheckMismatchedTypeAwareAllocators(OO_Array_New, OO_Array_Delete);
 }
 
 /// Look up the special member function that would be called by a special
@@ -9849,10 +9892,15 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
   //    results in an ambiguity or in a function that is deleted or inaccessible
   if (CSM == CXXSpecialMemberKind::Destructor && MD->isVirtual()) {
     FunctionDecl *OperatorDelete = nullptr;
+    QualType DeallocType = Context.getRecordType(RD);
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
+    ImplicitDeallocationParameters IDP = {
+        DeallocType, ShouldUseTypeAwareOperatorNewOrDelete(),
+        AlignedAllocationMode::No, SizedDeallocationMode::No};
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
-                                 OperatorDelete, /*Diagnose*/false)) {
+                                 OperatorDelete, IDP,
+                                 /*Diagnose=*/false)) {
       if (Diagnose)
         Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete);
       return true;
@@ -11021,14 +11069,18 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
       // conversion from 'this' to the type of a destroying operator delete's
       // first parameter, perform that conversion now.
       if (OperatorDelete->isDestroyingOperatorDelete()) {
-        QualType ParamType = OperatorDelete->getParamDecl(0)->getType();
+        unsigned AddressParamIndex = 0;
+        if (OperatorDelete->isTypeAwareOperatorNewOrDelete())
+          ++AddressParamIndex;
+        QualType ParamType =
+            OperatorDelete->getParamDecl(AddressParamIndex)->getType();
         if (!declaresSameEntity(ParamType->getAsCXXRecordDecl(), RD)) {
           // C++ [class.dtor]p13:
           //   ... as if for the expression 'delete this' appearing in a
           //   non-virtual destructor of the destructor's class.
           ContextRAII SwitchContext(*this, Destructor);
-          ExprResult This =
-              ActOnCXXThis(OperatorDelete->getParamDecl(0)->getLocation());
+          ExprResult This = ActOnCXXThis(
+              OperatorDelete->getParamDecl(AddressParamIndex)->getLocation());
           assert(!This.isInvalid() && "couldn't form 'this' expr in dtor?");
           This = PerformImplicitConversion(This.get(), ParamType,
                                            AssignmentAction::Passing);
@@ -11902,6 +11954,7 @@ NamespaceDecl *Sema::getStdNamespace() const {
   return cast_or_null<NamespaceDecl>(
                                  StdNamespace.get(Context.getExternalSource()));
 }
+
 namespace {
 
 enum UnsupportedSTLSelect {
@@ -12058,25 +12111,40 @@ NamespaceDecl *Sema::getOrCreateStdNamespace() {
   return getStdNamespace();
 }
 
-bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
-  assert(getLangOpts().CPlusPlus &&
-         "Looking for std::initializer_list outside of C++.");
-
+static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
+                               const char *ClassName,
+                               ClassTemplateDecl **CachedDecl,
+                               const Decl **MalformedDecl) {
   // We're looking for implicit instantiations of
-  // template <typename E> class std::initializer_list.
+  // template <typename U> class std::{ClassName}.
 
-  if (!StdNamespace) // If we haven't seen namespace std yet, this can't be it.
+  if (!S.StdNamespace) // If we haven't seen namespace std yet, this can't be
+                       // it.
     return false;
 
+  auto ReportMatchingNameAsMalformed = [&](NamedDecl *D) {
+    if (!MalformedDecl)
+      return;
+    if (!D)
+      D = SugaredType->getAsTagDecl();
+    if (!D || !D->isInStdNamespace())
+      return;
+    IdentifierInfo *II = D->getDeclName().getAsIdentifierInfo();
+    if (II && II == &S.PP.getIdentifierTable().get(ClassName))
+      *MalformedDecl = D;
+  };
+
   ClassTemplateDecl *Template = nullptr;
   const TemplateArgument *Arguments = nullptr;
 
+  QualType Ty = S.Context.getCanonicalType(SugaredType);
   if (const RecordType *RT = Ty->getAs<RecordType>()) {
-
     ClassTemplateSpecializationDecl *Specialization =
         dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
-    if (!Specialization)
+    if (!Specialization) {
+      ReportMatchingNameAsMalformed(RT->getDecl());
       return false;
+    }
 
     Template = Specialization->getSpecializedTemplate();
     Arguments = Specialization->getTemplateArgs().data();
@@ -12092,91 +12160,146 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
       Arguments = TST->template_arguments().begin();
     }
   }
-  if (!Template)
+  if (!Template) {
+    ReportMatchingNameAsMalformed(Ty->getAsTagDecl());
     return false;
+  }
 
-  if (!StdInitializerList) {
-    // Haven't recognized std::initializer_list yet, maybe this is it.
+  if (!*CachedDecl) {
+    // Haven't recognized std::{ClassName} yet, maybe this is it.
+    // FIXME: It seems we should just reuse LookupStdClassTemplate but the
+    // semantics of this are slightly 
diff erent, most notably the existing
+    // "lookup" semantics explicitly diagnose an invalid definition as an
+    // error.
     CXXRecordDecl *TemplateClass = Template->getTemplatedDecl();
     if (TemplateClass->getIdentifier() !=
-            &PP.getIdentifierTable().get("initializer_list") ||
-        !getStdNamespace()->InEnclosingNamespaceSetOf(
+            &S.PP.getIdentifierTable().get(ClassName) ||
+        !S.getStdNamespace()->InEnclosingNamespaceSetOf(
             TemplateClass->getNonTransparentDeclContext()))
       return false;
-    // This is a template called std::initializer_list, but is it the right
+    // This is a template called std::{ClassName}, but is it the right
     // template?
     TemplateParameterList *Params = Template->getTemplateParameters();
-    if (Params->getMinRequiredArguments() != 1)
-      return false;
-    if (!isa<TemplateTypeParmDecl>(Params->getParam(0)))
+    if (Params->getMinRequiredArguments() != 1 ||
+        !isa<TemplateTypeParmDecl>(Params->getParam(0))) {
+      if (MalformedDecl)
+        *MalformedDecl = TemplateClass;
       return false;
+    }
 
     // It's the right template.
-    StdInitializerList = Template;
+    *CachedDecl = Template;
   }
 
-  if (Template->getCanonicalDecl() != StdInitializerList->getCanonicalDecl())
+  if (Template->getCanonicalDecl() != (*CachedDecl)->getCanonicalDecl())
     return false;
 
-  // This is an instance of std::initializer_list. Find the argument type.
-  if (Element)
-    *Element = Arguments[0].getAsType();
+  // This is an instance of std::{ClassName}. Find the argument type.
+  if (TypeArg)
+    *TypeArg = Arguments[0].getAsType();
+
   return true;
 }
 
-static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){
-  NamespaceDecl *Std = S.getStdNamespace();
-  if (!Std) {
-    S.Diag(Loc, diag::err_implied_std_initializer_list_not_found);
+bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
+  assert(getLangOpts().CPlusPlus &&
+         "Looking for std::initializer_list outside of C++.");
+
+  // We're looking for implicit instantiations of
+  // template <typename E> class std::initializer_list.
+
+  return isStdClassTemplate(*this, Ty, Element, "initializer_list",
+                            &StdInitializerList, /*MalformedDecl=*/nullptr);
+}
+
+bool Sema::isStdTypeIdentity(QualType Ty, QualType *Element,
+                             const Decl **MalformedDecl) {
+  assert(getLangOpts().CPlusPlus &&
+         "Looking for std::type_identity outside of C++.");
+
+  // We're looking for implicit instantiations of
+  // template <typename T> struct std::type_identity.
+
+  return isStdClassTemplate(*this, Ty, Element, "type_identity",
+                            &StdTypeIdentity, MalformedDecl);
+}
+
+static ClassTemplateDecl *LookupStdClassTemplate(Sema &S, SourceLocation Loc,
+                                                 const char *ClassName,
+                                                 bool *WasMalformed) {
+  if (!S.StdNamespace)
     return nullptr;
-  }
 
-  LookupResult Result(S, &S.PP.getIdentifierTable().get("initializer_list"),
-                      Loc, Sema::LookupOrdinaryName);
-  if (!S.LookupQualifiedName(Result, Std)) {
-    S.Diag(Loc, diag::err_implied_std_initializer_list_not_found);
+  LookupResult Result(S, &S.PP.getIdentifierTable().get(ClassName), Loc,
+                      Sema::LookupOrdinaryName);
+  if (!S.LookupQualifiedName(Result, S.getStdNamespace()))
     return nullptr;
-  }
+
   ClassTemplateDecl *Template = Result.getAsSingle<ClassTemplateDecl>();
   if (!Template) {
     Result.suppressDiagnostics();
     // We found something weird. Complain about the first thing we found.
     NamedDecl *Found = *Result.begin();
-    S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list);
+    S.Diag(Found->getLocation(), diag::err_malformed_std_class_template)
+        << ClassName;
+    if (WasMalformed)
+      *WasMalformed = true;
     return nullptr;
   }
 
-  // We found some template called std::initializer_list. Now verify that it's
+  // We found some template with the correct name. Now verify that it's
   // correct.
   TemplateParameterList *Params = Template->getTemplateParameters();
   if (Params->getMinRequiredArguments() != 1 ||
       !isa<TemplateTypeParmDecl>(Params->getParam(0))) {
-    S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list);
+    S.Diag(Template->getLocation(), diag::err_malformed_std_class_template)
+        << ClassName;
+    if (WasMalformed)
+      *WasMalformed = true;
     return nullptr;
   }
 
   return Template;
 }
 
-QualType Sema::BuildStdInitializerList(QualType Element, SourceLocation Loc) {
-  if (!StdInitializerList) {
-    StdInitializerList = LookupStdInitializerList(*this, Loc);
-    if (!StdInitializerList)
-      return QualType();
-  }
-
+static QualType BuildStdClassTemplate(Sema &S, ClassTemplateDecl *CTD,
+                                      QualType TypeParam, SourceLocation Loc) {
+  assert(S.getStdNamespace());
   TemplateArgumentListInfo Args(Loc, Loc);
-  Args.addArgument(TemplateArgumentLoc(TemplateArgument(Element),
-                                       Context.getTrivialTypeSourceInfo(Element,
-                                                                        Loc)));
+  auto TSI = S.Context.getTrivialTypeSourceInfo(TypeParam, Loc);
+  Args.addArgument(TemplateArgumentLoc(TemplateArgument(TypeParam), TSI));
 
-  QualType T = CheckTemplateIdType(TemplateName(StdInitializerList), Loc, Args);
+  QualType T = S.CheckTemplateIdType(TemplateName(CTD), Loc, Args);
   if (T.isNull())
     return QualType();
 
-  return Context.getElaboratedType(
+  return S.Context.getElaboratedType(
       ElaboratedTypeKeyword::None,
-      NestedNameSpecifier::Create(Context, nullptr, getStdNamespace()), T);
+      NestedNameSpecifier::Create(S.Context, nullptr, S.getStdNamespace()), T);
+}
+
+QualType Sema::BuildStdInitializerList(QualType Element, SourceLocation Loc) {
+  if (!StdInitializerList) {
+    bool WasMalformed = false;
+    StdInitializerList =
+        LookupStdClassTemplate(*this, Loc, "initializer_list", &WasMalformed);
+    if (!StdInitializerList) {
+      if (!WasMalformed)
+        Diag(Loc, diag::err_implied_std_initializer_list_not_found);
+      return QualType();
+    }
+  }
+  return BuildStdClassTemplate(*this, StdInitializerList, Element, Loc);
+}
+
+QualType Sema::tryBuildStdTypeIdentity(QualType Type, SourceLocation Loc) {
+  if (!StdTypeIdentity) {
+    StdTypeIdentity = LookupStdClassTemplate(*this, Loc, "type_identity",
+                                             /*WasMalformed=*/nullptr);
+    if (!StdTypeIdentity)
+      return QualType();
+  }
+  return BuildStdClassTemplate(*this, StdTypeIdentity, Type, Loc);
 }
 
 bool Sema::isInitListConstructor(const FunctionDecl *Ctor) {
@@ -16288,6 +16411,65 @@ bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
   return Invalid;
 }
 
+TypeAwareAllocationMode Sema::ShouldUseTypeAwareOperatorNewOrDelete() const {
+  bool SeenTypedOperators = Context.hasSeenTypeAwareOperatorNewOrDelete();
+  return typeAwareAllocationModeFromBool(SeenTypedOperators);
+}
+
+FunctionDecl *
+Sema::BuildTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
+                                QualType DeallocType, SourceLocation Loc) {
+  if (DeallocType.isNull())
+    return nullptr;
+
+  FunctionDecl *FnDecl = FnTemplateDecl->getTemplatedDecl();
+  if (!FnDecl->isTypeAwareOperatorNewOrDelete())
+    return nullptr;
+
+  if (FnDecl->isVariadic())
+    return nullptr;
+
+  unsigned NumParams = FnDecl->getNumParams();
+  constexpr unsigned RequiredParameterCount =
+      FunctionDecl::RequiredTypeAwareDeleteParameterCount;
+  // A usual deallocation function has no placement parameters
+  if (NumParams != RequiredParameterCount)
+    return nullptr;
+
+  // A type aware allocation is only usual if the only dependent parameter is
+  // the first parameter.
+  if (llvm::any_of(FnDecl->parameters().drop_front(),
+                   [](const ParmVarDecl *ParamDecl) {
+                     return ParamDecl->getType()->isDependentType();
+                   }))
+    return nullptr;
+
+  QualType SpecializedTypeIdentity = tryBuildStdTypeIdentity(DeallocType, Loc);
+  if (SpecializedTypeIdentity.isNull())
+    return nullptr;
+
+  SmallVector<QualType, RequiredParameterCount> ArgTypes;
+  ArgTypes.reserve(NumParams);
+
+  // The first parameter to a type aware operator delete is by definition the
+  // type-identity argument, so we explicitly set this to the target
+  // type-identity type, the remaining usual parameters should then simply match
+  // the type declared in the function template.
+  ArgTypes.push_back(SpecializedTypeIdentity);
+  for (unsigned ParamIdx = 1; ParamIdx < RequiredParameterCount; ++ParamIdx)
+    ArgTypes.push_back(FnDecl->getParamDecl(ParamIdx)->getType());
+
+  FunctionProtoType::ExtProtoInfo EPI;
+  QualType ExpectedFunctionType =
+      Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
+  sema::TemplateDeductionInfo Info(Loc);
+  FunctionDecl *Result;
+  if (DeduceTemplateArguments(FnTemplateDecl, nullptr, ExpectedFunctionType,
+                              Result, Info) != TemplateDeductionResult::Success)
+    return nullptr;
+  return Result;
+}
+
 static inline bool
 CheckOperatorNewDeleteDeclarationScope(Sema &SemaRef,
                                        const FunctionDecl *FnDecl) {
@@ -16317,79 +16499,184 @@ static CanQualType RemoveAddressSpaceFromPtr(Sema &SemaRef,
       PtrTy->getPointeeType().getUnqualifiedType(), PtrQuals)));
 }
 
-static inline bool
-CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
-                            CanQualType ExpectedResultType,
-                            CanQualType ExpectedFirstParamType,
-                            unsigned DependentParamTypeDiag,
-                            unsigned InvalidParamTypeDiag) {
-  QualType ResultType =
-      FnDecl->getType()->castAs<FunctionType>()->getReturnType();
+enum class AllocationOperatorKind { New, Delete };
+
+static bool IsPotentiallyTypeAwareOperatorNewOrDelete(Sema &SemaRef,
+                                                      const FunctionDecl *FD,
+                                                      bool *WasMalformed) {
+  const Decl *MalformedDecl = nullptr;
+  if (FD->getNumParams() > 0 &&
+      SemaRef.isStdTypeIdentity(FD->getParamDecl(0)->getType(),
+                                /*TypeArgument=*/nullptr, &MalformedDecl))
+    return true;
+
+  if (!MalformedDecl)
+    return false;
 
-  if (SemaRef.getLangOpts().OpenCLCPlusPlus) {
-    // The operator is valid on any address space for OpenCL.
-    // Drop address space from actual and expected result types.
-    if (const auto *PtrTy = ResultType->getAs<PointerType>())
-      ResultType = RemoveAddressSpaceFromPtr(SemaRef, PtrTy);
+  if (WasMalformed)
+    *WasMalformed = true;
 
-    if (auto ExpectedPtrTy = ExpectedResultType->getAs<PointerType>())
-      ExpectedResultType = RemoveAddressSpaceFromPtr(SemaRef, ExpectedPtrTy);
+  return true;
+}
+
+static bool isDestroyingDeleteT(QualType Type) {
+  auto *RD = Type->getAsCXXRecordDecl();
+  return RD && RD->isInStdNamespace() && RD->getIdentifier() &&
+         RD->getIdentifier()->isStr("destroying_delete_t");
+}
+
+static bool IsPotentiallyDestroyingOperatorDelete(Sema &SemaRef,
+                                                  const FunctionDecl *FD) {
+  // C++ P0722:
+  //   Within a class C, a single object deallocation function with signature
+  //     (T, std::destroying_delete_t, <more params>)
+  //   is a destroying operator delete.
+  bool IsPotentiallyTypeAware = IsPotentiallyTypeAwareOperatorNewOrDelete(
+      SemaRef, FD, /*WasMalformed=*/nullptr);
+  unsigned DestroyingDeleteIdx = IsPotentiallyTypeAware + /* address */ 1;
+  return isa<CXXMethodDecl>(FD) && FD->getOverloadedOperator() == OO_Delete &&
+         FD->getNumParams() > DestroyingDeleteIdx &&
+         isDestroyingDeleteT(FD->getParamDecl(DestroyingDeleteIdx)->getType());
+}
+
+static inline bool CheckOperatorNewDeleteTypes(
+    Sema &SemaRef, FunctionDecl *FnDecl, AllocationOperatorKind OperatorKind,
+    CanQualType ExpectedResultType, CanQualType ExpectedSizeOrAddressParamType,
+    unsigned DependentParamTypeDiag, unsigned InvalidParamTypeDiag) {
+  auto NormalizeType = [&SemaRef](QualType T) {
+    if (SemaRef.getLangOpts().OpenCLCPlusPlus) {
+      // The operator is valid on any address space for OpenCL.
+      // Drop address space from actual and expected result types.
+      if (const auto PtrTy = T->template getAs<PointerType>())
+        T = RemoveAddressSpaceFromPtr(SemaRef, PtrTy);
+    }
+    return SemaRef.Context.getCanonicalType(T);
+  };
+
+  const unsigned NumParams = FnDecl->getNumParams();
+  unsigned FirstNonTypeParam = 0;
+  bool MalformedTypeIdentity = false;
+  bool IsPotentiallyTypeAware = IsPotentiallyTypeAwareOperatorNewOrDelete(
+      SemaRef, FnDecl, &MalformedTypeIdentity);
+  unsigned MinimumMandatoryArgumentCount = 1;
+  unsigned SizeParameterIndex = 0;
+  if (IsPotentiallyTypeAware) {
+    // We don't emit this diagnosis for template instantiations as we will
+    // have already emitted it for the original template declaration.
+    if (!FnDecl->isTemplateInstantiation()) {
+      unsigned DiagID = SemaRef.getLangOpts().CPlusPlus26
+                            ? diag::warn_cxx26_type_aware_allocators
+                            : diag::ext_cxx26_type_aware_allocators;
+      SemaRef.Diag(FnDecl->getLocation(), DiagID);
+    }
+
+    if (OperatorKind == AllocationOperatorKind::New) {
+      SizeParameterIndex = 1;
+      MinimumMandatoryArgumentCount =
+          FunctionDecl::RequiredTypeAwareNewParameterCount;
+    } else {
+      SizeParameterIndex = 2;
+      MinimumMandatoryArgumentCount =
+          FunctionDecl::RequiredTypeAwareDeleteParameterCount;
+    }
+    FirstNonTypeParam = 1;
+  }
+
+  bool IsPotentiallyDestroyingDelete =
+      IsPotentiallyDestroyingOperatorDelete(SemaRef, FnDecl);
+
+  if (IsPotentiallyDestroyingDelete) {
+    ++MinimumMandatoryArgumentCount;
+    ++SizeParameterIndex;
   }
 
+  if (NumParams < MinimumMandatoryArgumentCount)
+    return SemaRef.Diag(FnDecl->getLocation(),
+                        diag::err_operator_new_delete_too_few_parameters)
+           << IsPotentiallyTypeAware << IsPotentiallyDestroyingDelete
+           << FnDecl->getDeclName() << MinimumMandatoryArgumentCount;
+
+  for (unsigned Idx = 0; Idx < MinimumMandatoryArgumentCount; ++Idx) {
+    const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(Idx);
+    if (ParamDecl->hasDefaultArg())
+      return SemaRef.Diag(FnDecl->getLocation(),
+                          diag::err_operator_new_default_arg)
+             << FnDecl->getDeclName() << Idx << ParamDecl->getDefaultArgRange();
+  }
+
+  auto *FnType = FnDecl->getType()->castAs<FunctionType>();
+  QualType CanResultType = NormalizeType(FnType->getReturnType());
+  QualType CanExpectedResultType = NormalizeType(ExpectedResultType);
+  QualType CanExpectedSizeOrAddressParamType =
+      NormalizeType(ExpectedSizeOrAddressParamType);
+
   // Check that the result type is what we expect.
-  if (SemaRef.Context.getCanonicalType(ResultType) != ExpectedResultType) {
+  if (CanResultType != CanExpectedResultType) {
     // Reject even if the type is dependent; an operator delete function is
     // required to have a non-dependent result type.
     return SemaRef.Diag(
                FnDecl->getLocation(),
-               ResultType->isDependentType()
+               CanResultType->isDependentType()
                    ? diag::err_operator_new_delete_dependent_result_type
                    : diag::err_operator_new_delete_invalid_result_type)
            << FnDecl->getDeclName() << ExpectedResultType;
   }
 
   // A function template must have at least 2 parameters.
-  if (FnDecl->getDescribedFunctionTemplate() && FnDecl->getNumParams() < 2)
+  if (FnDecl->getDescribedFunctionTemplate() && NumParams < 2)
     return SemaRef.Diag(FnDecl->getLocation(),
                       diag::err_operator_new_delete_template_too_few_parameters)
         << FnDecl->getDeclName();
 
-  // The function decl must have at least 1 parameter.
-  if (FnDecl->getNumParams() == 0)
-    return SemaRef.Diag(FnDecl->getLocation(),
-                        diag::err_operator_new_delete_too_few_parameters)
-      << FnDecl->getDeclName();
+  auto CheckType = [&](unsigned ParamIdx, QualType ExpectedType,
+                       auto FallbackType) -> bool {
+    const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(ParamIdx);
+    if (ExpectedType.isNull()) {
+      return SemaRef.Diag(FnDecl->getLocation(), InvalidParamTypeDiag)
+             << IsPotentiallyTypeAware << IsPotentiallyDestroyingDelete
+             << FnDecl->getDeclName() << (1 + ParamIdx) << FallbackType
+             << ParamDecl->getSourceRange();
+    }
+    CanQualType CanExpectedTy =
+        NormalizeType(SemaRef.Context.getCanonicalType(ExpectedType));
+    auto ActualParamType =
+        NormalizeType(ParamDecl->getType().getUnqualifiedType());
+    if (ActualParamType == CanExpectedTy)
+      return false;
+    unsigned Diagnostic = ActualParamType->isDependentType()
+                              ? DependentParamTypeDiag
+                              : InvalidParamTypeDiag;
+    return SemaRef.Diag(FnDecl->getLocation(), Diagnostic)
+           << IsPotentiallyTypeAware << IsPotentiallyDestroyingDelete
+           << FnDecl->getDeclName() << (1 + ParamIdx) << ExpectedType
+           << FallbackType << ParamDecl->getSourceRange();
+  };
 
-  QualType FirstParamType = FnDecl->getParamDecl(0)->getType();
-  if (SemaRef.getLangOpts().OpenCLCPlusPlus) {
-    // The operator is valid on any address space for OpenCL.
-    // Drop address space from actual and expected first parameter types.
-    if (const auto *PtrTy =
-            FnDecl->getParamDecl(0)->getType()->getAs<PointerType>())
-      FirstParamType = RemoveAddressSpaceFromPtr(SemaRef, PtrTy);
+  // Check that the first parameter type is what we expect.
+  if (CheckType(FirstNonTypeParam, CanExpectedSizeOrAddressParamType, "size_t"))
+    return true;
 
-    if (auto ExpectedPtrTy = ExpectedFirstParamType->getAs<PointerType>())
-      ExpectedFirstParamType =
-          RemoveAddressSpaceFromPtr(SemaRef, ExpectedPtrTy);
-  }
+  FnDecl->setIsDestroyingOperatorDelete(IsPotentiallyDestroyingDelete);
 
-  // Check that the first parameter type is what we expect.
-  if (SemaRef.Context.getCanonicalType(FirstParamType).getUnqualifiedType() !=
-      ExpectedFirstParamType) {
-    // The first parameter type is not allowed to be dependent. As a tentative
-    // DR resolution, we allow a dependent parameter type if it is the right
-    // type anyway, to allow destroying operator delete in class templates.
-    return SemaRef.Diag(FnDecl->getLocation(), FirstParamType->isDependentType()
-                                                   ? DependentParamTypeDiag
-                                                   : InvalidParamTypeDiag)
-           << FnDecl->getDeclName() << ExpectedFirstParamType;
-  }
+  // If the first parameter type is not a type-identity we're done, otherwise
+  // we need to ensure the size and alignment parameters have the correct type
+  if (!IsPotentiallyTypeAware)
+    return false;
 
-  return false;
+  if (CheckType(SizeParameterIndex, SemaRef.Context.getSizeType(), "size_t"))
+    return true;
+  TypeDecl *StdAlignValTDecl = SemaRef.getStdAlignValT();
+  QualType StdAlignValT =
+      StdAlignValTDecl ? SemaRef.Context.getTypeDeclType(StdAlignValTDecl)
+                       : QualType();
+  if (CheckType(SizeParameterIndex + 1, StdAlignValT, "std::align_val_t"))
+    return true;
+
+  FnDecl->setIsTypeAwareOperatorNewOrDelete();
+  return MalformedTypeIdentity;
 }
 
-static bool
-CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
+static bool CheckOperatorNewDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
   // C++ [basic.stc.dynamic.allocation]p1:
   //   A program is ill-formed if an allocation function is declared in a
   //   namespace scope other than global scope or declared static in global
@@ -16403,20 +16690,10 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
   // C++ [basic.stc.dynamic.allocation]p1:
   //  The return type shall be void*. The first parameter shall have type
   //  std::size_t.
-  if (CheckOperatorNewDeleteTypes(SemaRef, FnDecl, SemaRef.Context.VoidPtrTy,
-                                  SizeTy,
-                                  diag::err_operator_new_dependent_param_type,
-                                  diag::err_operator_new_param_type))
-    return true;
-
-  // C++ [basic.stc.dynamic.allocation]p1:
-  //  The first parameter shall not have an associated default argument.
-  if (FnDecl->getParamDecl(0)->hasDefaultArg())
-    return SemaRef.Diag(FnDecl->getLocation(),
-                        diag::err_operator_new_default_arg)
-      << FnDecl->getDeclName() << FnDecl->getParamDecl(0)->getDefaultArgRange();
-
-  return false;
+  return CheckOperatorNewDeleteTypes(
+      SemaRef, FnDecl, AllocationOperatorKind::New, SemaRef.Context.VoidPtrTy,
+      SizeTy, diag::err_operator_new_dependent_param_type,
+      diag::err_operator_new_param_type);
 }
 
 static bool
@@ -16429,13 +16706,48 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
     return true;
 
   auto *MD = dyn_cast<CXXMethodDecl>(FnDecl);
+  auto ConstructDestroyingDeleteAddressType = [&]() {
+    assert(MD);
+    return SemaRef.Context.getCanonicalType(SemaRef.Context.getPointerType(
+        SemaRef.Context.getRecordType(MD->getParent())));
+  };
+
+  // C++ P2719: A destroying operator delete cannot be type aware
+  // so for QoL we actually check for this explicitly by considering
+  // an destroying-delete appropriate address type and the presence of
+  // any parameter of type destroying_delete_t as an erroneous attempt
+  // to declare a type aware destroying delete, rather than emitting a
+  // pile of incorrect parameter type errors.
+  if (MD && IsPotentiallyTypeAwareOperatorNewOrDelete(
+                SemaRef, MD, /*WasMalformed=*/nullptr)) {
+    QualType AddressParamType =
+        SemaRef.Context.getCanonicalType(MD->getParamDecl(1)->getType());
+    if (AddressParamType != SemaRef.Context.VoidPtrTy &&
+        AddressParamType == ConstructDestroyingDeleteAddressType()) {
+      // The address parameter type implies an author trying to construct a
+      // type aware destroying delete, so we'll see if we can find a parameter
+      // of type `std::destroying_delete_t`, and if we find it we'll report
+      // this as being an attempt at a type aware destroying delete just stop
+      // here. If we don't do this, the resulting incorrect parameter ordering
+      // results in a pile mismatched argument type errors that don't explain
+      // the core problem.
+      for (auto Param : MD->parameters()) {
+        if (isDestroyingDeleteT(Param->getType())) {
+          SemaRef.Diag(MD->getLocation(),
+                       diag::err_type_aware_destroying_operator_delete)
+              << Param->getSourceRange();
+          return true;
+        }
+      }
+    }
+  }
 
   // C++ P0722:
   //   Within a class C, the first parameter of a destroying operator delete
   //   shall be of type C *. The first parameter of any other deallocation
   //   function shall be of type void *.
-  CanQualType ExpectedFirstParamType =
-      MD && MD->isDestroyingOperatorDelete()
+  CanQualType ExpectedAddressParamType =
+      MD && IsPotentiallyDestroyingOperatorDelete(SemaRef, MD)
           ? SemaRef.Context.getCanonicalType(SemaRef.Context.getPointerType(
                 SemaRef.Context.getRecordType(MD->getParent())))
           : SemaRef.Context.VoidPtrTy;
@@ -16443,7 +16755,8 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
   // C++ [basic.stc.dynamic.deallocation]p2:
   //   Each deallocation function shall return void
   if (CheckOperatorNewDeleteTypes(
-          SemaRef, FnDecl, SemaRef.Context.VoidTy, ExpectedFirstParamType,
+          SemaRef, FnDecl, AllocationOperatorKind::Delete,
+          SemaRef.Context.VoidTy, ExpectedAddressParamType,
           diag::err_operator_delete_dependent_param_type,
           diag::err_operator_delete_param_type))
     return true;
@@ -16451,11 +16764,12 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
   // C++ P0722:
   //   A destroying operator delete shall be a usual deallocation function.
   if (MD && !MD->getParent()->isDependentContext() &&
-      MD->isDestroyingOperatorDelete() &&
-      !SemaRef.isUsualDeallocationFunction(MD)) {
-    SemaRef.Diag(MD->getLocation(),
-                 diag::err_destroying_operator_delete_not_usual);
-    return true;
+      MD->isDestroyingOperatorDelete()) {
+    if (!SemaRef.isUsualDeallocationFunction(MD)) {
+      SemaRef.Diag(MD->getLocation(),
+                   diag::err_destroying_operator_delete_not_usual);
+      return true;
+    }
   }
 
   return false;

diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 247cd02b23522..a87cf6087b71d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1728,12 +1728,14 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
   if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD))
     return S.isUsualDeallocationFunction(Method);
 
-  if (FD->getOverloadedOperator() != OO_Delete &&
-      FD->getOverloadedOperator() != OO_Array_Delete)
+  if (!FD->getDeclName().isAnyOperatorDelete())
     return false;
 
-  unsigned UsualParams = 1;
+  if (FD->isTypeAwareOperatorNewOrDelete())
+    return FunctionDecl::RequiredTypeAwareDeleteParameterCount ==
+           FD->getNumParams();
 
+  unsigned UsualParams = 1;
   if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() &&
       S.Context.hasSameUnqualifiedType(
           FD->getParamDecl(UsualParams)->getType(),
@@ -1751,15 +1753,56 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
 
 namespace {
   struct UsualDeallocFnInfo {
-    UsualDeallocFnInfo() : Found(), FD(nullptr) {}
-    UsualDeallocFnInfo(Sema &S, DeclAccessPair Found)
+    UsualDeallocFnInfo()
+        : Found(), FD(nullptr),
+          IDP(AlignedAllocationMode::No, SizedDeallocationMode::No) {}
+    UsualDeallocFnInfo(Sema &S, DeclAccessPair Found, QualType AllocType,
+                       SourceLocation Loc)
         : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
-          Destroying(false), HasSizeT(false), HasAlignValT(false),
+          Destroying(false),
+          IDP({AllocType, TypeAwareAllocationMode::No,
+               AlignedAllocationMode::No, SizedDeallocationMode::No}),
           CUDAPref(SemaCUDA::CFP_Native) {
-      // A function template declaration is never a usual deallocation function.
-      if (!FD)
-        return;
+      // A function template declaration is only a usual deallocation function
+      // if it is a typed delete.
+      if (!FD) {
+        if (AllocType.isNull())
+          return;
+        auto *FTD = dyn_cast<FunctionTemplateDecl>(Found->getUnderlyingDecl());
+        if (!FTD)
+          return;
+        FunctionDecl *InstantiatedDecl =
+            S.BuildTypeAwareUsualDelete(FTD, AllocType, Loc);
+        if (!InstantiatedDecl)
+          return;
+        FD = InstantiatedDecl;
+      }
       unsigned NumBaseParams = 1;
+      if (FD->isTypeAwareOperatorNewOrDelete()) {
+        // If this is a type aware operator delete we instantiate an appropriate
+        // specialization of std::type_identity<>. If we do not know the
+        // type being deallocated, or if the type-identity parameter of the
+        // deallocation function does not match the constructed type_identity
+        // specialization we reject the declaration.
+        if (AllocType.isNull()) {
+          FD = nullptr;
+          return;
+        }
+        QualType TypeIdentityTag = FD->getParamDecl(0)->getType();
+        QualType ExpectedTypeIdentityTag =
+            S.tryBuildStdTypeIdentity(AllocType, Loc);
+        if (ExpectedTypeIdentityTag.isNull()) {
+          FD = nullptr;
+          return;
+        }
+        if (!S.Context.hasSameType(TypeIdentityTag, ExpectedTypeIdentityTag)) {
+          FD = nullptr;
+          return;
+        }
+        IDP.PassTypeIdentity = TypeAwareAllocationMode::Yes;
+        ++NumBaseParams;
+      }
+
       if (FD->isDestroyingOperatorDelete()) {
         Destroying = true;
         ++NumBaseParams;
@@ -1770,13 +1813,13 @@ namespace {
               FD->getParamDecl(NumBaseParams)->getType(),
               S.Context.getSizeType())) {
         ++NumBaseParams;
-        HasSizeT = true;
+        IDP.PassSize = SizedDeallocationMode::Yes;
       }
 
       if (NumBaseParams < FD->getNumParams() &&
           FD->getParamDecl(NumBaseParams)->getType()->isAlignValT()) {
         ++NumBaseParams;
-        HasAlignValT = true;
+        IDP.PassAlignment = AlignedAllocationMode::Yes;
       }
 
       // In CUDA, determine how much we'd like / dislike to call this.
@@ -1787,31 +1830,69 @@ namespace {
 
     explicit operator bool() const { return FD; }
 
-    bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize,
-                      bool WantAlign) const {
+    int Compare(Sema &S, const UsualDeallocFnInfo &Other,
+                ImplicitDeallocationParameters TargetIDP) const {
+      assert(!TargetIDP.Type.isNull() ||
+             !isTypeAwareAllocation(Other.IDP.PassTypeIdentity));
+
       // C++ P0722:
       //   A destroying operator delete is preferred over a non-destroying
       //   operator delete.
       if (Destroying != Other.Destroying)
-        return Destroying;
+        return Destroying ? 1 : -1;
+
+      const ImplicitDeallocationParameters &OtherIDP = Other.IDP;
+      // Selection for type awareness has priority over alignment and size
+      if (IDP.PassTypeIdentity != OtherIDP.PassTypeIdentity)
+        return IDP.PassTypeIdentity == TargetIDP.PassTypeIdentity ? 1 : -1;
 
       // C++17 [expr.delete]p10:
       //   If the type has new-extended alignment, a function with a parameter
       //   of type std::align_val_t is preferred; otherwise a function without
       //   such a parameter is preferred
-      if (HasAlignValT != Other.HasAlignValT)
-        return HasAlignValT == WantAlign;
-
-      if (HasSizeT != Other.HasSizeT)
-        return HasSizeT == WantSize;
+      if (IDP.PassAlignment != OtherIDP.PassAlignment)
+        return IDP.PassAlignment == TargetIDP.PassAlignment ? 1 : -1;
+
+      if (IDP.PassSize != OtherIDP.PassSize)
+        return IDP.PassSize == TargetIDP.PassSize ? 1 : -1;
+
+      if (isTypeAwareAllocation(IDP.PassTypeIdentity)) {
+        // Type aware allocation involves templates so we need to choose
+        // the best type
+        FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
+        FunctionTemplateDecl *OtherPrimaryTemplate =
+            Other.FD->getPrimaryTemplate();
+        if ((!PrimaryTemplate) != (!OtherPrimaryTemplate))
+          return OtherPrimaryTemplate ? 1 : -1;
+
+        if (PrimaryTemplate && OtherPrimaryTemplate) {
+          const auto *DC = dyn_cast<CXXRecordDecl>(Found->getDeclContext());
+          const auto *OtherDC =
+              dyn_cast<CXXRecordDecl>(Other.Found->getDeclContext());
+          unsigned ImplicitArgCount = Destroying + IDP.getNumImplicitArgs();
+          if (FunctionTemplateDecl *Best = S.getMoreSpecializedTemplate(
+                  PrimaryTemplate, OtherPrimaryTemplate, SourceLocation(),
+                  TPOC_Call, ImplicitArgCount,
+                  DC ? QualType(DC->getTypeForDecl(), 0) : QualType{},
+                  OtherDC ? QualType(OtherDC->getTypeForDecl(), 0) : QualType{},
+                  false)) {
+            return Best == PrimaryTemplate ? 1 : -1;
+          }
+        }
+      }
 
       // Use CUDA call preference as a tiebreaker.
-      return CUDAPref > Other.CUDAPref;
+      if (CUDAPref > Other.CUDAPref)
+        return 1;
+      if (CUDAPref == Other.CUDAPref)
+        return 0;
+      return -1;
     }
 
     DeclAccessPair Found;
     FunctionDecl *FD;
-    bool Destroying, HasSizeT, HasAlignValT;
+    bool Destroying;
+    ImplicitDeallocationParameters IDP;
     SemaCUDA::CUDAFunctionPreference CUDAPref;
   };
 }
@@ -1826,32 +1907,62 @@ static bool hasNewExtendedAlignment(Sema &S, QualType AllocType) {
              S.getASTContext().getTargetInfo().getNewAlign();
 }
 
+static bool CheckDeleteOperator(Sema &S, SourceLocation StartLoc,
+                                SourceRange Range, bool Diagnose,
+                                CXXRecordDecl *NamingClass, DeclAccessPair Decl,
+                                FunctionDecl *Operator) {
+  if (Operator->isTypeAwareOperatorNewOrDelete()) {
+    QualType SelectedTypeIdentityParameter =
+        Operator->getParamDecl(0)->getType();
+    if (S.RequireCompleteType(StartLoc, SelectedTypeIdentityParameter,
+                              diag::err_incomplete_type))
+      return true;
+  }
+
+  // FIXME: DiagnoseUseOfDecl?
+  if (Operator->isDeleted()) {
+    if (Diagnose) {
+      StringLiteral *Msg = Operator->getDeletedMessage();
+      S.Diag(StartLoc, diag::err_deleted_function_use)
+          << (Msg != nullptr) << (Msg ? Msg->getString() : StringRef());
+      S.NoteDeletedFunction(Operator);
+    }
+    return true;
+  }
+
+  return S.CheckAllocationAccess(StartLoc, Range, NamingClass, Decl, Diagnose);
+}
+
 /// Select the correct "usual" deallocation function to use from a selection of
 /// deallocation functions (either global or class-scope).
 static UsualDeallocFnInfo resolveDeallocationOverload(
-    Sema &S, LookupResult &R, bool WantSize, bool WantAlign,
+    Sema &S, LookupResult &R, const ImplicitDeallocationParameters &IDP,
+    SourceLocation Loc,
     llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) {
-  UsualDeallocFnInfo Best;
 
+  UsualDeallocFnInfo Best;
   for (auto I = R.begin(), E = R.end(); I != E; ++I) {
-    UsualDeallocFnInfo Info(S, I.getPair());
+    UsualDeallocFnInfo Info(S, I.getPair(), IDP.Type, Loc);
     if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD) ||
         Info.CUDAPref == SemaCUDA::CFP_Never)
       continue;
 
+    if (!isTypeAwareAllocation(IDP.PassTypeIdentity) &&
+        isTypeAwareAllocation(Info.IDP.PassTypeIdentity))
+      continue;
     if (!Best) {
       Best = Info;
       if (BestFns)
         BestFns->push_back(Info);
       continue;
     }
-
-    if (Best.isBetterThan(Info, WantSize, WantAlign))
+    int ComparisonResult = Best.Compare(S, Info, IDP);
+    if (ComparisonResult > 0)
       continue;
 
     //   If more than one preferred function is found, all non-preferred
     //   functions are eliminated from further consideration.
-    if (BestFns && Info.isBetterThan(Best, WantSize, WantAlign))
+    if (BestFns && ComparisonResult < 0)
       BestFns->clear();
 
     Best = Info;
@@ -1867,6 +1978,7 @@ static UsualDeallocFnInfo resolveDeallocationOverload(
 /// we need to store the array size (even if the type is
 /// trivially-destructible).
 static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
+                                         TypeAwareAllocationMode PassType,
                                          QualType allocType) {
   const RecordType *record =
     allocType->getBaseElementTypeUnsafe()->getAs<RecordType>();
@@ -1892,10 +2004,12 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
   // C++17 [expr.delete]p10:
   //   If the deallocation functions have class scope, the one without a
   //   parameter of type std::size_t is selected.
-  auto Best = resolveDeallocationOverload(
-      S, ops, /*WantSize*/false,
-      /*WantAlign*/hasNewExtendedAlignment(S, allocType));
-  return Best && Best.HasSizeT;
+  ImplicitDeallocationParameters IDP = {
+      allocType, PassType,
+      alignedAllocationModeFromBool(hasNewExtendedAlignment(S, allocType)),
+      SizedDeallocationMode::No};
+  auto Best = resolveDeallocationOverload(S, ops, IDP, loc);
+  return Best && isSizedDeallocation(Best.IDP.PassSize);
 }
 
 ExprResult
@@ -2012,8 +2126,7 @@ void Sema::diagnoseUnavailableAlignedAllocation(const FunctionDecl &FD,
         getASTContext().getTargetInfo().getPlatformName());
     VersionTuple OSVersion = alignedAllocMinVersion(T.getOS());
 
-    OverloadedOperatorKind Kind = FD.getDeclName().getCXXOverloadedOperator();
-    bool IsDelete = Kind == OO_Delete || Kind == OO_Array_Delete;
+    bool IsDelete = FD.getDeclName().isAnyOperatorDelete();
     Diag(Loc, diag::err_aligned_allocation_unavailable)
         << IsDelete << FD.getType().getAsString() << OSName
         << OSVersion.getAsString() << OSVersion.empty();
@@ -2307,27 +2420,31 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
   unsigned Alignment =
       AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
   unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
-  bool PassAlignment = getLangOpts().AlignedAllocation &&
-                       Alignment > NewAlignment;
+  ImplicitAllocationParameters IAP = {
+      AllocType, ShouldUseTypeAwareOperatorNewOrDelete(),
+      alignedAllocationModeFromBool(getLangOpts().AlignedAllocation &&
+                                    Alignment > NewAlignment)};
 
   if (CheckArgsForPlaceholders(PlacementArgs))
     return ExprError();
 
   AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both;
+  SourceRange AllocationParameterRange = Range;
+  if (PlacementLParen.isValid() && PlacementRParen.isValid())
+    AllocationParameterRange = SourceRange(PlacementLParen, PlacementRParen);
   if (!AllocType->isDependentType() &&
       !Expr::hasAnyTypeDependentArguments(PlacementArgs) &&
-      FindAllocationFunctions(
-          StartLoc, SourceRange(PlacementLParen, PlacementRParen), Scope, Scope,
-          AllocType, ArraySize.has_value(), PassAlignment, PlacementArgs,
-          OperatorNew, OperatorDelete))
+      FindAllocationFunctions(StartLoc, AllocationParameterRange, Scope, Scope,
+                              AllocType, ArraySize.has_value(), IAP,
+                              PlacementArgs, OperatorNew, OperatorDelete))
     return ExprError();
 
   // If this is an array allocation, compute whether the usual array
   // deallocation function for the type has a size_t parameter.
   bool UsualArrayDeleteWantsSize = false;
   if (ArraySize && !AllocType->isDependentType())
-    UsualArrayDeleteWantsSize =
-        doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType);
+    UsualArrayDeleteWantsSize = doesUsualArrayDeleteWantSize(
+        *this, StartLoc, IAP.PassTypeIdentity, AllocType);
 
   SmallVector<Expr *, 8> AllPlaceArgs;
   if (OperatorNew) {
@@ -2339,10 +2456,16 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     // arguments. Skip the first parameter because we don't have a corresponding
     // argument. Skip the second parameter too if we're passing in the
     // alignment; we've already filled it in.
-    unsigned NumImplicitArgs = PassAlignment ? 2 : 1;
-    if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto,
-                               NumImplicitArgs, PlacementArgs, AllPlaceArgs,
-                               CallType))
+    unsigned NumImplicitArgs = 1;
+    if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+      assert(OperatorNew->isTypeAwareOperatorNewOrDelete());
+      NumImplicitArgs++;
+    }
+    if (isAlignedAllocation(IAP.PassAlignment))
+      NumImplicitArgs++;
+    if (GatherArgumentsForCall(AllocationParameterRange.getBegin(), OperatorNew,
+                               Proto, NumImplicitArgs, PlacementArgs,
+                               AllPlaceArgs, CallType))
       return ExprError();
 
     if (!AllPlaceArgs.empty())
@@ -2377,10 +2500,10 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
 
     IntegerLiteral AllocationSizeLiteral(
         Context, AllocationSize.value_or(llvm::APInt::getZero(SizeTyWidth)),
-        SizeTy, SourceLocation());
+        SizeTy, StartLoc);
     // Otherwise, if we failed to constant-fold the allocation size, we'll
     // just give up and pass-in something opaque, that isn't a null pointer.
-    OpaqueValueExpr OpaqueAllocationSize(SourceLocation(), SizeTy, VK_PRValue,
+    OpaqueValueExpr OpaqueAllocationSize(StartLoc, SizeTy, VK_PRValue,
                                          OK_Ordinary, /*SourceExpr=*/nullptr);
 
     // Let's synthesize the alignment argument in case we will need it.
@@ -2393,7 +2516,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
         Context,
         llvm::APInt(Context.getTypeSize(SizeTy),
                     Alignment / Context.getCharWidth()),
-        SizeTy, SourceLocation());
+        SizeTy, StartLoc);
     ImplicitCastExpr DesiredAlignment(ImplicitCastExpr::OnStack, AlignValT,
                                       CK_IntegralCast, &AlignmentLiteral,
                                       VK_PRValue, FPOptionsOverride());
@@ -2404,7 +2527,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     CallArgs.emplace_back(AllocationSize
                               ? static_cast<Expr *>(&AllocationSizeLiteral)
                               : &OpaqueAllocationSize);
-    if (PassAlignment)
+    if (isAlignedAllocation(IAP.PassAlignment))
       CallArgs.emplace_back(&DesiredAlignment);
     CallArgs.insert(CallArgs.end(), PlacementArgs.begin(), PlacementArgs.end());
 
@@ -2415,7 +2538,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
 
     // Warn if the type is over-aligned and is being allocated by (unaligned)
     // global operator new.
-    if (PlacementArgs.empty() && !PassAlignment &&
+    if (PlacementArgs.empty() && !isAlignedAllocation(IAP.PassAlignment) &&
         (OperatorNew->isImplicit() ||
          (OperatorNew->getBeginLoc().isValid() &&
           getSourceManager().isInSystemHeader(OperatorNew->getBeginLoc())))) {
@@ -2502,10 +2625,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
   }
 
   return CXXNewExpr::Create(Context, UseGlobal, OperatorNew, OperatorDelete,
-                            PassAlignment, UsualArrayDeleteWantsSize,
-                            PlacementArgs, TypeIdParens, ArraySize, InitStyle,
-                            Initializer, ResultType, AllocTypeInfo, Range,
-                            DirectInitRange);
+                            IAP, UsualArrayDeleteWantsSize, PlacementArgs,
+                            TypeIdParens, ArraySize, InitStyle, Initializer,
+                            ResultType, AllocTypeInfo, Range, DirectInitRange);
 }
 
 bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
@@ -2546,10 +2668,17 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
   return false;
 }
 
-static bool resolveAllocationOverload(
-    Sema &S, LookupResult &R, SourceRange Range, SmallVectorImpl<Expr *> &Args,
-    bool &PassAlignment, FunctionDecl *&Operator,
-    OverloadCandidateSet *AlignedCandidates, Expr *AlignArg, bool Diagnose) {
+enum class ResolveMode { Typed, Untyped };
+static bool resolveAllocationOverloadInterior(
+    Sema &S, LookupResult &R, SourceRange Range, ResolveMode Mode,
+    SmallVectorImpl<Expr *> &Args, AlignedAllocationMode &PassAlignment,
+    FunctionDecl *&Operator, OverloadCandidateSet *AlignedCandidates,
+    Expr *AlignArg, bool Diagnose) {
+  unsigned NonTypeArgumentOffset = 0;
+  if (Mode == ResolveMode::Typed) {
+    ++NonTypeArgumentOffset;
+  }
+
   OverloadCandidateSet Candidates(R.getNameLoc(),
                                   OverloadCandidateSet::CSK_Normal);
   for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end();
@@ -2557,6 +2686,9 @@ static bool resolveAllocationOverload(
     // Even member operator new/delete are implicitly treated as
     // static, so don't use AddMemberCandidate.
     NamedDecl *D = (*Alloc)->getUnderlyingDecl();
+    bool IsTypeAware = D->getAsFunction()->isTypeAwareOperatorNewOrDelete();
+    if (IsTypeAware == (Mode != ResolveMode::Typed))
+      continue;
 
     if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
       S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(),
@@ -2590,13 +2722,13 @@ static bool resolveAllocationOverload(
     //   If no matching function is found and the allocated object type has
     //   new-extended alignment, the alignment argument is removed from the
     //   argument list, and overload resolution is performed again.
-    if (PassAlignment) {
-      PassAlignment = false;
-      AlignArg = Args[1];
-      Args.erase(Args.begin() + 1);
-      return resolveAllocationOverload(S, R, Range, Args, PassAlignment,
-                                       Operator, &Candidates, AlignArg,
-                                       Diagnose);
+    if (isAlignedAllocation(PassAlignment)) {
+      PassAlignment = AlignedAllocationMode::No;
+      AlignArg = Args[NonTypeArgumentOffset + 1];
+      Args.erase(Args.begin() + NonTypeArgumentOffset + 1);
+      return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
+                                               PassAlignment, Operator,
+                                               &Candidates, AlignArg, Diagnose);
     }
 
     // MSVC will fall back on trying to find a matching global operator new
@@ -2606,16 +2738,22 @@ static bool resolveAllocationOverload(
     // FIXME: Find out how this interacts with the std::align_val_t fallback
     // once MSVC implements it.
     if (R.getLookupName().getCXXOverloadedOperator() == OO_Array_New &&
-        S.Context.getLangOpts().MSVCCompat) {
+        S.Context.getLangOpts().MSVCCompat && Mode != ResolveMode::Typed) {
       R.clear();
       R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
       S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl());
       // FIXME: This will give bad diagnostics pointing at the wrong functions.
-      return resolveAllocationOverload(S, R, Range, Args, PassAlignment,
-                                       Operator, /*Candidates=*/nullptr,
-                                       /*AlignArg=*/nullptr, Diagnose);
+      return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
+                                               PassAlignment, Operator,
+                                               /*Candidates=*/nullptr,
+                                               /*AlignArg=*/nullptr, Diagnose);
+    }
+    if (Mode == ResolveMode::Typed) {
+      // If we can't find a matching type aware operator we don't consider this
+      // a failure.
+      Operator = nullptr;
+      return false;
     }
-
     if (Diagnose) {
       // If this is an allocation of the form 'new (p) X' for some object
       // pointer p (or an expression that will decay to such a pointer),
@@ -2639,16 +2777,21 @@ static bool resolveAllocationOverload(
       SmallVector<OverloadCandidate*, 32> AlignedCands;
       llvm::SmallVector<Expr*, 4> AlignedArgs;
       if (AlignedCandidates) {
-        auto IsAligned = [](OverloadCandidate &C) {
-          return C.Function->getNumParams() > 1 &&
-                 C.Function->getParamDecl(1)->getType()->isAlignValT();
+        auto IsAligned = [NonTypeArgumentOffset](OverloadCandidate &C) {
+          auto AlignArgOffset = NonTypeArgumentOffset + 1;
+          return C.Function->getNumParams() > AlignArgOffset &&
+                 C.Function->getParamDecl(AlignArgOffset)
+                     ->getType()
+                     ->isAlignValT();
         };
         auto IsUnaligned = [&](OverloadCandidate &C) { return !IsAligned(C); };
 
-        AlignedArgs.reserve(Args.size() + 1);
-        AlignedArgs.push_back(Args[0]);
+        AlignedArgs.reserve(Args.size() + NonTypeArgumentOffset + 1);
+        for (unsigned Idx = 0; Idx < NonTypeArgumentOffset + 1; ++Idx)
+          AlignedArgs.push_back(Args[Idx]);
         AlignedArgs.push_back(AlignArg);
-        AlignedArgs.append(Args.begin() + 1, Args.end());
+        AlignedArgs.append(Args.begin() + NonTypeArgumentOffset + 1,
+                           Args.end());
         AlignedCands = AlignedCandidates->CompleteCandidates(
             S, OCD_AllCandidates, AlignedArgs, R.getNameLoc(), IsAligned);
 
@@ -2688,14 +2831,75 @@ static bool resolveAllocationOverload(
   llvm_unreachable("Unreachable, bad result from BestViableFunction");
 }
 
-bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
-                                   AllocationFunctionScope NewScope,
-                                   AllocationFunctionScope DeleteScope,
-                                   QualType AllocType, bool IsArray,
-                                   bool &PassAlignment, MultiExprArg PlaceArgs,
-                                   FunctionDecl *&OperatorNew,
-                                   FunctionDecl *&OperatorDelete,
-                                   bool Diagnose) {
+enum class DeallocLookupMode { Untyped, OptionallyTyped };
+
+static void LookupGlobalDeallocationFunctions(Sema &S, SourceLocation Loc,
+                                              LookupResult &FoundDelete,
+                                              DeallocLookupMode Mode,
+                                              DeclarationName Name) {
+  S.LookupQualifiedName(FoundDelete, S.Context.getTranslationUnitDecl());
+  if (Mode != DeallocLookupMode::OptionallyTyped) {
+    // We're going to remove either the typed or the non-typed
+    bool RemoveTypedDecl = Mode == DeallocLookupMode::Untyped;
+    LookupResult::Filter Filter = FoundDelete.makeFilter();
+    while (Filter.hasNext()) {
+      FunctionDecl *FD = Filter.next()->getUnderlyingDecl()->getAsFunction();
+      if (FD->isTypeAwareOperatorNewOrDelete() == RemoveTypedDecl)
+        Filter.erase();
+    }
+    Filter.done();
+  }
+}
+
+static bool resolveAllocationOverload(
+    Sema &S, LookupResult &R, SourceRange Range, SmallVectorImpl<Expr *> &Args,
+    ImplicitAllocationParameters &IAP, FunctionDecl *&Operator,
+    OverloadCandidateSet *AlignedCandidates, Expr *AlignArg, bool Diagnose) {
+  Operator = nullptr;
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+    assert(S.isStdTypeIdentity(Args[0]->getType(), nullptr));
+    // The internal overload resolution work mutates the argument list
+    // in accordance with the spec. We may want to change that in future,
+    // but for now we deal with this by making a copy of the non-type-identity
+    // arguments.
+    SmallVector<Expr *> UntypedParameters;
+    UntypedParameters.reserve(Args.size() - 1);
+    UntypedParameters.push_back(Args[1]);
+    // Type aware allocation implicitly includes the alignment parameter so
+    // only include it in the untyped parameter list if alignment was explicitly
+    // requested
+    if (isAlignedAllocation(IAP.PassAlignment))
+      UntypedParameters.push_back(Args[2]);
+    UntypedParameters.append(Args.begin() + 3, Args.end());
+
+    AlignedAllocationMode InitialAlignmentMode = IAP.PassAlignment;
+    IAP.PassAlignment = AlignedAllocationMode::Yes;
+    if (resolveAllocationOverloadInterior(
+            S, R, Range, ResolveMode::Typed, Args, IAP.PassAlignment, Operator,
+            AlignedCandidates, AlignArg, Diagnose))
+      return true;
+    if (Operator)
+      return false;
+
+    // If we got to this point we could not find a matching typed operator
+    // so we update the IAP flags, and revert to our stored copy of the
+    // type-identity-less argument list.
+    IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
+    IAP.PassAlignment = InitialAlignmentMode;
+    Args = UntypedParameters;
+  }
+  assert(!S.isStdTypeIdentity(Args[0]->getType(), nullptr));
+  return resolveAllocationOverloadInterior(
+      S, R, Range, ResolveMode::Untyped, Args, IAP.PassAlignment, Operator,
+      AlignedCandidates, AlignArg, Diagnose);
+}
+
+bool Sema::FindAllocationFunctions(
+    SourceLocation StartLoc, SourceRange Range,
+    AllocationFunctionScope NewScope, AllocationFunctionScope DeleteScope,
+    QualType AllocType, bool IsArray, ImplicitAllocationParameters &IAP,
+    MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew,
+    FunctionDecl *&OperatorDelete, bool Diagnose) {
   // --- Choosing an allocation function ---
   // C++ 5.3.4p8 - 14 & 18
   // 1) If looking in AFS_Global scope for allocation functions, only look in
@@ -2707,12 +2911,44 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   //   placement form.
 
   SmallVector<Expr*, 8> AllocArgs;
-  AllocArgs.reserve((PassAlignment ? 2 : 1) + PlaceArgs.size());
+  AllocArgs.reserve(IAP.getNumImplicitArgs() + PlaceArgs.size());
+
+  // C++ [expr.new]p8:
+  //   If the allocated type is a non-array type, the allocation
+  //   function's name is operator new and the deallocation function's
+  //   name is operator delete. If the allocated type is an array
+  //   type, the allocation function's name is operator new[] and the
+  //   deallocation function's name is operator delete[].
+  DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
+      IsArray ? OO_Array_New : OO_New);
+
+  QualType AllocElemType = Context.getBaseElementType(AllocType);
 
   // We don't care about the actual value of these arguments.
   // FIXME: Should the Sema create the expression and embed it in the syntax
   // tree? Or should the consumer just recalculate the value?
   // FIXME: Using a dummy value will interact poorly with attribute enable_if.
+
+  // We use size_t as a stand in so that we can construct the init
+  // expr on the stack
+  QualType TypeIdentity = Context.getSizeType();
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+    QualType SpecializedTypeIdentity =
+        tryBuildStdTypeIdentity(IAP.Type, StartLoc);
+    if (!SpecializedTypeIdentity.isNull()) {
+      TypeIdentity = SpecializedTypeIdentity;
+      if (RequireCompleteType(StartLoc, TypeIdentity,
+                              diag::err_incomplete_type))
+        return true;
+    } else
+      IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
+  }
+  TypeAwareAllocationMode OriginalTypeAwareState = IAP.PassTypeIdentity;
+
+  CXXScalarValueInitExpr TypeIdentityParam(TypeIdentity, nullptr, StartLoc);
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity))
+    AllocArgs.push_back(&TypeIdentityParam);
+
   QualType SizeTy = Context.getSizeType();
   unsigned SizeTyWidth = Context.getTypeSize(SizeTy);
   IntegerLiteral Size(Context, llvm::APInt::getZero(SizeTyWidth), SizeTy,
@@ -2720,27 +2956,18 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   AllocArgs.push_back(&Size);
 
   QualType AlignValT = Context.VoidTy;
-  if (PassAlignment) {
+  bool IncludeAlignParam = isAlignedAllocation(IAP.PassAlignment) ||
+                           isTypeAwareAllocation(IAP.PassTypeIdentity);
+  if (IncludeAlignParam) {
     DeclareGlobalNewDelete();
     AlignValT = Context.getTypeDeclType(getStdAlignValT());
   }
   CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
-  if (PassAlignment)
+  if (IncludeAlignParam)
     AllocArgs.push_back(&Align);
 
   AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end());
 
-  // C++ [expr.new]p8:
-  //   If the allocated type is a non-array type, the allocation
-  //   function's name is operator new and the deallocation function's
-  //   name is operator delete. If the allocated type is an array
-  //   type, the allocation function's name is operator new[] and the
-  //   deallocation function's name is operator delete[].
-  DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
-      IsArray ? OO_Array_New : OO_New);
-
-  QualType AllocElemType = Context.getBaseElementType(AllocType);
-
   // Find the allocation function.
   {
     LookupResult R(*this, NewName, StartLoc, LookupOrdinaryName);
@@ -2783,8 +3010,8 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     // We do our own custom access checks below.
     R.suppressDiagnostics();
 
-    if (resolveAllocationOverload(*this, R, Range, AllocArgs, PassAlignment,
-                                  OperatorNew, /*Candidates=*/nullptr,
+    if (resolveAllocationOverload(*this, R, Range, AllocArgs, IAP, OperatorNew,
+                                  /*Candidates=*/nullptr,
                                   /*AlignArg=*/nullptr, Diagnose))
       return true;
   }
@@ -2846,6 +3073,23 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   }
 
   bool FoundGlobalDelete = FoundDelete.empty();
+  bool IsClassScopedTypeAwareNew =
+      isTypeAwareAllocation(IAP.PassTypeIdentity) &&
+      OperatorNew->getDeclContext()->isRecord();
+  auto DiagnoseMissingTypeAwareCleanupOperator = [&](bool IsPlacementOperator) {
+    assert(isTypeAwareAllocation(IAP.PassTypeIdentity));
+    if (Diagnose) {
+      Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
+          << OperatorNew->getDeclName() << IsPlacementOperator << DeleteName;
+      Diag(OperatorNew->getLocation(), diag::note_type_aware_operator_declared)
+          << OperatorNew->isTypeAwareOperatorNewOrDelete()
+          << OperatorNew->getDeclName() << OperatorNew->getDeclContext();
+    }
+  };
+  if (IsClassScopedTypeAwareNew && FoundDelete.empty()) {
+    DiagnoseMissingTypeAwareCleanupOperator(/*isPlacementNew=*/false);
+    return true;
+  }
   if (FoundDelete.empty()) {
     FoundDelete.clear(LookupOrdinaryName);
 
@@ -2853,7 +3097,11 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
       return true;
 
     DeclareGlobalNewDelete();
-    LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
+    DeallocLookupMode LookupMode = isTypeAwareAllocation(OriginalTypeAwareState)
+                                       ? DeallocLookupMode::OptionallyTyped
+                                       : DeallocLookupMode::Untyped;
+    LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
+                                      DeleteName);
   }
 
   FoundDelete.suppressDiagnostics();
@@ -2873,7 +3121,13 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   // FIXME: Should (size_t, std::align_val_t) also be considered non-placement?
   // This affects whether an exception from the constructor of an overaligned
   // type uses the sized or non-sized form of aligned operator delete.
-  bool isPlacementNew = !PlaceArgs.empty() || OperatorNew->param_size() != 1 ||
+
+  unsigned NonPlacementNewArgCount = 1; // size parameter
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity))
+    NonPlacementNewArgCount =
+        /* type-identity */ 1 + /* size */ 1 + /* alignment */ 1;
+  bool isPlacementNew = !PlaceArgs.empty() ||
+                        OperatorNew->param_size() != NonPlacementNewArgCount ||
                         OperatorNew->isVariadic();
 
   if (isPlacementNew) {
@@ -2891,9 +3145,16 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     {
       auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
 
-      SmallVector<QualType, 4> ArgTypes;
+      SmallVector<QualType, 6> ArgTypes;
+      int InitialParamOffset = 0;
+      if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+        ArgTypes.push_back(TypeIdentity);
+        InitialParamOffset = 1;
+      }
       ArgTypes.push_back(Context.VoidPtrTy);
-      for (unsigned I = 1, N = Proto->getNumParams(); I < N; ++I)
+      for (unsigned I = ArgTypes.size() - InitialParamOffset,
+                    N = Proto->getNumParams();
+           I < N; ++I)
         ArgTypes.push_back(Proto->getParamType(I));
 
       FunctionProtoType::ExtProtoInfo EPI;
@@ -2929,6 +3190,10 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     if (getLangOpts().CUDA)
       CUDA().EraseUnwantedMatches(getCurFunctionDecl(/*AllowLambda=*/true),
                                   Matches);
+    if (Matches.empty() && isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+      DiagnoseMissingTypeAwareCleanupOperator(isPlacementNew);
+      return true;
+    }
   } else {
     // C++1y [expr.new]p22:
     //   For a non-placement allocation function, the normal deallocation
@@ -2938,11 +3203,14 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     // without a size_t argument, but prefers a non-member operator delete
     // with a size_t where possible (which it always is in this case).
     llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns;
+    ImplicitDeallocationParameters IDP = {
+        AllocElemType, OriginalTypeAwareState,
+        alignedAllocationModeFromBool(
+            hasNewExtendedAlignment(*this, AllocElemType)),
+        sizedDeallocationModeFromBool(FoundGlobalDelete)};
     UsualDeallocFnInfo Selected = resolveDeallocationOverload(
-        *this, FoundDelete, /*WantSize*/ FoundGlobalDelete,
-        /*WantAlign*/ hasNewExtendedAlignment(*this, AllocElemType),
-        &BestDeallocFns);
-    if (Selected)
+        *this, FoundDelete, IDP, StartLoc, &BestDeallocFns);
+    if (Selected && BestDeallocFns.empty())
       Matches.push_back(std::make_pair(Selected.Found, Selected.FD));
     else {
       // If we failed to select an operator, all remaining functions are viable
@@ -2958,6 +3226,35 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   //   deallocation function will be called.
   if (Matches.size() == 1) {
     OperatorDelete = Matches[0].second;
+    bool FoundTypeAwareOperator =
+        OperatorDelete->isTypeAwareOperatorNewOrDelete() ||
+        OperatorNew->isTypeAwareOperatorNewOrDelete();
+    if (Diagnose && FoundTypeAwareOperator) {
+      bool MismatchedTypeAwareness =
+          OperatorDelete->isTypeAwareOperatorNewOrDelete() !=
+          OperatorNew->isTypeAwareOperatorNewOrDelete();
+      bool MismatchedContext =
+          OperatorDelete->getDeclContext() != OperatorNew->getDeclContext();
+      if (MismatchedTypeAwareness || MismatchedContext) {
+        FunctionDecl *Operators[] = {OperatorDelete, OperatorNew};
+        bool TypeAwareOperatorIndex =
+            OperatorNew->isTypeAwareOperatorNewOrDelete();
+        Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
+            << Operators[TypeAwareOperatorIndex]->getDeclName()
+            << isPlacementNew
+            << Operators[!TypeAwareOperatorIndex]->getDeclName()
+            << Operators[TypeAwareOperatorIndex]->getDeclContext();
+        Diag(OperatorNew->getLocation(),
+             diag::note_type_aware_operator_declared)
+            << OperatorNew->isTypeAwareOperatorNewOrDelete()
+            << OperatorNew->getDeclName() << OperatorNew->getDeclContext();
+        Diag(OperatorDelete->getLocation(),
+             diag::note_type_aware_operator_declared)
+            << OperatorDelete->isTypeAwareOperatorNewOrDelete()
+            << OperatorDelete->getDeclName()
+            << OperatorDelete->getDeclContext();
+      }
+    }
 
     // C++1z [expr.new]p23:
     //   If the lookup finds a usual deallocation function (3.7.4.2)
@@ -2968,22 +3265,26 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     if (getLangOpts().CPlusPlus11 && isPlacementNew &&
         isNonPlacementDeallocationFunction(*this, OperatorDelete)) {
       UsualDeallocFnInfo Info(*this,
-                              DeclAccessPair::make(OperatorDelete, AS_public));
+                              DeclAccessPair::make(OperatorDelete, AS_public),
+                              AllocElemType, StartLoc);
       // Core issue, per mail to core reflector, 2016-10-09:
       //   If this is a member operator delete, and there is a corresponding
       //   non-sized member operator delete, this isn't /really/ a sized
       //   deallocation function, it just happens to have a size_t parameter.
-      bool IsSizedDelete = Info.HasSizeT;
+      bool IsSizedDelete = isSizedDeallocation(Info.IDP.PassSize);
       if (IsSizedDelete && !FoundGlobalDelete) {
-        auto NonSizedDelete =
-            resolveDeallocationOverload(*this, FoundDelete, /*WantSize*/false,
-                                        /*WantAlign*/Info.HasAlignValT);
-        if (NonSizedDelete && !NonSizedDelete.HasSizeT &&
-            NonSizedDelete.HasAlignValT == Info.HasAlignValT)
+        ImplicitDeallocationParameters SizeTestingIDP = {
+            AllocElemType, Info.IDP.PassTypeIdentity, Info.IDP.PassAlignment,
+            SizedDeallocationMode::No};
+        auto NonSizedDelete = resolveDeallocationOverload(
+            *this, FoundDelete, SizeTestingIDP, StartLoc);
+        if (NonSizedDelete &&
+            !isSizedDeallocation(NonSizedDelete.IDP.PassSize) &&
+            NonSizedDelete.IDP.PassAlignment == Info.IDP.PassAlignment)
           IsSizedDelete = false;
       }
 
-      if (IsSizedDelete) {
+      if (IsSizedDelete && !isTypeAwareAllocation(IAP.PassTypeIdentity)) {
         SourceRange R = PlaceArgs.empty()
                             ? SourceRange()
                             : SourceRange(PlaceArgs.front()->getBeginLoc(),
@@ -2994,9 +3295,11 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
               << DeleteName;
       }
     }
+    if (CheckDeleteOperator(*this, StartLoc, Range, Diagnose,
+                            FoundDelete.getNamingClass(), Matches[0].first,
+                            Matches[0].second))
+      return true;
 
-    CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(),
-                          Matches[0].first);
   } else if (!Matches.empty()) {
     // We found multiple suitable operators. Per [expr.new]p20, that means we
     // call no 'operator delete' function, but we should at least warn the user.
@@ -3175,9 +3478,7 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
       /*IsVariadic=*/false, /*IsCXXMethod=*/false, /*IsBuiltin=*/true));
 
   QualType BadAllocType;
-  bool HasBadAllocExceptionSpec
-    = (Name.getCXXOverloadedOperator() == OO_New ||
-       Name.getCXXOverloadedOperator() == OO_Array_New);
+  bool HasBadAllocExceptionSpec = Name.isAnyOperatorNew();
   if (HasBadAllocExceptionSpec) {
     if (!getLangOpts().CPlusPlus11) {
       BadAllocType = Context.getTypeDeclType(getStdBadAlloc());
@@ -3257,21 +3558,29 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
   }
 }
 
-FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
-                                                  bool CanProvideSize,
-                                                  bool Overaligned,
-                                                  DeclarationName Name) {
+FunctionDecl *
+Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
+                                    ImplicitDeallocationParameters IDP,
+                                    DeclarationName Name) {
   DeclareGlobalNewDelete();
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
-  LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
+  LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete,
+                                    DeallocLookupMode::OptionallyTyped, Name);
 
   // FIXME: It's possible for this to result in ambiguity, through a
   // user-declared variadic operator delete or the enable_if attribute. We
   // should probably not consider those cases to be usual deallocation
   // functions. But for now we just make an arbitrary choice in that case.
-  auto Result = resolveDeallocationOverload(*this, FoundDelete, CanProvideSize,
-                                            Overaligned);
+  auto Result = resolveDeallocationOverload(*this, FoundDelete, IDP, StartLoc);
+  if (!Result)
+    return nullptr;
+
+  if (CheckDeleteOperator(*this, StartLoc, StartLoc, /*Diagnose=*/true,
+                          FoundDelete.getNamingClass(), Result.Found,
+                          Result.FD))
+    return nullptr;
+
   assert(Result.FD && "operator delete missing from global scope?");
   return Result.FD;
 }
@@ -3282,21 +3591,30 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
                                                           bool Diagnose) {
 
   FunctionDecl *OperatorDelete = nullptr;
-  if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, Diagnose))
+  QualType DeallocType = Context.getRecordType(RD);
+  ImplicitDeallocationParameters IDP = {
+      DeallocType, ShouldUseTypeAwareOperatorNewOrDelete(),
+      AlignedAllocationMode::No, SizedDeallocationMode::No};
+
+  if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, IDP, Diagnose))
     return nullptr;
+
   if (OperatorDelete)
     return OperatorDelete;
 
   // If there's no class-specific operator delete, look up the global delete.
-  return FindUsualDeallocationFunction(
-      Loc, true, hasNewExtendedAlignment(*this, Context.getRecordType(RD)),
-      Name);
+  QualType RecordType = Context.getRecordType(RD);
+  IDP.PassAlignment =
+      alignedAllocationModeFromBool(hasNewExtendedAlignment(*this, RecordType));
+  IDP.PassSize = SizedDeallocationMode::Yes;
+  return FindUsualDeallocationFunction(Loc, IDP, Name);
 }
 
 bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                     DeclarationName Name,
-                                    FunctionDecl *&Operator, bool Diagnose,
-                                    bool WantSize, bool WantAligned) {
+                                    FunctionDecl *&Operator,
+                                    ImplicitDeallocationParameters IDP,
+                                    bool Diagnose) {
   LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
   // Try to find operator delete/operator delete[] in class scope.
   LookupQualifiedName(Found, RD);
@@ -3309,36 +3627,22 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
 
   Found.suppressDiagnostics();
 
-  bool Overaligned =
-      WantAligned || hasNewExtendedAlignment(*this, Context.getRecordType(RD));
+  if (!isAlignedAllocation(IDP.PassAlignment) &&
+      hasNewExtendedAlignment(*this, Context.getRecordType(RD)))
+    IDP.PassAlignment = AlignedAllocationMode::Yes;
 
   // C++17 [expr.delete]p10:
   //   If the deallocation functions have class scope, the one without a
   //   parameter of type std::size_t is selected.
   llvm::SmallVector<UsualDeallocFnInfo, 4> Matches;
-  resolveDeallocationOverload(*this, Found, /*WantSize*/ WantSize,
-                              /*WantAlign*/ Overaligned, &Matches);
+  resolveDeallocationOverload(*this, Found, IDP, StartLoc, &Matches);
 
   // If we could find an overload, use it.
   if (Matches.size() == 1) {
     Operator = cast<CXXMethodDecl>(Matches[0].FD);
-
-    // FIXME: DiagnoseUseOfDecl?
-    if (Operator->isDeleted()) {
-      if (Diagnose) {
-        StringLiteral *Msg = Operator->getDeletedMessage();
-        Diag(StartLoc, diag::err_deleted_function_use)
-            << (Msg != nullptr) << (Msg ? Msg->getString() : StringRef());
-        NoteDeletedFunction(Operator);
-      }
-      return true;
-    }
-
-    if (CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(),
-                              Matches[0].Found, Diagnose) == AR_inaccessible)
-      return true;
-
-    return false;
+    return CheckDeleteOperator(*this, StartLoc, StartLoc, Diagnose,
+                               Found.getNamingClass(), Matches[0].Found,
+                               Operator);
   }
 
   // We found multiple suitable operators; complain about the ambiguity.
@@ -3762,9 +4066,12 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
                                       ArrayForm ? OO_Array_Delete : OO_Delete);
 
     if (PointeeRD) {
+      ImplicitDeallocationParameters IDP = {
+          Pointee, ShouldUseTypeAwareOperatorNewOrDelete(),
+          AlignedAllocationMode::No, SizedDeallocationMode::No};
       if (!UseGlobal &&
           FindDeallocationFunction(StartLoc, PointeeRD, DeleteName,
-                                   OperatorDelete))
+                                   OperatorDelete, IDP))
         return ExprError();
 
       // If we're allocating an array of records, check whether the
@@ -3773,16 +4080,17 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
         // If the user specifically asked to use the global allocator,
         // we'll need to do the lookup into the class.
         if (UseGlobal)
-          UsualArrayDeleteWantsSize =
-            doesUsualArrayDeleteWantSize(*this, StartLoc, PointeeElem);
+          UsualArrayDeleteWantsSize = doesUsualArrayDeleteWantSize(
+              *this, StartLoc, IDP.PassTypeIdentity, PointeeElem);
 
         // Otherwise, the usual operator delete[] should be the
         // function we just found.
-        else if (isa_and_nonnull<CXXMethodDecl>(OperatorDelete))
-          UsualArrayDeleteWantsSize =
-            UsualDeallocFnInfo(*this,
-                               DeclAccessPair::make(OperatorDelete, AS_public))
-              .HasSizeT;
+        else if (isa_and_nonnull<CXXMethodDecl>(OperatorDelete)) {
+          UsualDeallocFnInfo UDFI(
+              *this, DeclAccessPair::make(OperatorDelete, AS_public), Pointee,
+              StartLoc);
+          UsualArrayDeleteWantsSize = isSizedDeallocation(UDFI.IDP.PassSize);
+        }
       }
 
       if (!PointeeRD->hasIrrelevantDestructor()) {
@@ -3815,8 +4123,13 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
       bool Overaligned = hasNewExtendedAlignment(*this, Pointee);
 
       // Look for a global declaration.
-      OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize,
-                                                     Overaligned, DeleteName);
+      ImplicitDeallocationParameters IDP = {
+          Pointee, ShouldUseTypeAwareOperatorNewOrDelete(),
+          alignedAllocationModeFromBool(Overaligned),
+          sizedDeallocationModeFromBool(CanProvideSize)};
+      OperatorDelete = FindUsualDeallocationFunction(StartLoc, IDP, DeleteName);
+      if (!OperatorDelete)
+        return ExprError();
     }
 
     if (OperatorDelete->isInvalidDecl())
@@ -3838,11 +4151,21 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
 
     DiagnoseUseOfDecl(OperatorDelete, StartLoc);
 
+    unsigned AddressParamIdx = 0;
+    if (OperatorDelete->isTypeAwareOperatorNewOrDelete()) {
+      QualType TypeIdentity = OperatorDelete->getParamDecl(0)->getType();
+      if (RequireCompleteType(StartLoc, TypeIdentity,
+                              diag::err_incomplete_type))
+        return ExprError();
+      AddressParamIdx = 1;
+    }
+
     // Convert the operand to the type of the first parameter of operator
     // delete. This is only necessary if we selected a destroying operator
     // delete that we are going to call (non-virtually); converting to void*
     // is trivial and left to AST consumers to handle.
-    QualType ParamType = OperatorDelete->getParamDecl(0)->getType();
+    QualType ParamType =
+        OperatorDelete->getParamDecl(AddressParamIdx)->getType();
     if (!IsVirtualDelete && !ParamType->getPointeeType()->isVoidType()) {
       Qualifiers Qs = Pointee.getQualifiers();
       if (Qs.hasCVRQualifiers()) {

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 3f619b3fb3daa..108d7e1dbaebc 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2738,6 +2738,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
     LexicalDC = SemaRef.CurContext;
   }
 
+  Function->setIsDestroyingOperatorDelete(D->isDestroyingOperatorDelete());
+  Function->setIsTypeAwareOperatorNewOrDelete(
+      D->isTypeAwareOperatorNewOrDelete());
   Function->setLexicalDeclContext(LexicalDC);
 
   // Attach the parameters

diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 22fe54b526433..715aaf6452264 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1927,6 +1927,7 @@ void ASTStmtReader::VisitCXXNewExpr(CXXNewExpr *E) {
 
   E->CXXNewExprBits.IsGlobalNew = Record.readInt();
   E->CXXNewExprBits.ShouldPassAlignment = Record.readInt();
+  E->CXXNewExprBits.ShouldPassTypeIdentity = Record.readInt();
   E->CXXNewExprBits.UsualArrayDeleteWantsSize = Record.readInt();
   E->CXXNewExprBits.HasInitializer = Record.readInt();
   E->CXXNewExprBits.StoredInitializationStyle = Record.readInt();

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index d0a0f843c7542..939b37c547349 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1932,7 +1932,9 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
   Record.push_back(E->isParenTypeId());
 
   Record.push_back(E->isGlobalNew());
-  Record.push_back(E->passAlignment());
+  ImplicitAllocationParameters IAP = E->implicitAllocationParameters();
+  Record.push_back(isAlignedAllocation(IAP.PassAlignment));
+  Record.push_back(isTypeAwareAllocation(IAP.PassTypeIdentity));
   Record.push_back(E->doesUsualArrayDeleteWantSize());
   Record.push_back(E->CXXNewExprBits.HasInitializer);
   Record.push_back(E->CXXNewExprBits.StoredInitializationStyle);

diff  --git a/clang/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.allocation/p1.cpp b/clang/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.allocation/p1.cpp
index 3b77a62ce7d6f..2ba212b2cedf2 100644
--- a/clang/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.allocation/p1.cpp
+++ b/clang/test/CXX/basic/basic.stc/basic.stc.dynamic/basic.stc.dynamic.allocation/p1.cpp
@@ -37,7 +37,7 @@ struct G {
 };
 
 struct H {
-  template<typename T> void *operator new(T, int); // expected-error {{'operator new' cannot take a dependent type as first parameter; use size_t}}
+  template<typename T> void *operator new(T, int); // expected-error {{'operator new' cannot take a dependent type as its 1st parameter; use size_t}}
 };
 
 struct I {

diff  --git a/clang/test/CodeGenCXX/Inputs/std-coroutine.h b/clang/test/CodeGenCXX/Inputs/std-coroutine.h
new file mode 100644
index 0000000000000..0bc459d48ccc6
--- /dev/null
+++ b/clang/test/CodeGenCXX/Inputs/std-coroutine.h
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
+#ifndef STD_COROUTINE_H
+#define STD_COROUTINE_H
+
+namespace std {
+
+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>
+typename remove_reference<T>::type &&move(T &&t) noexcept;
+
+struct input_iterator_tag {};
+struct forward_iterator_tag : public input_iterator_tag {};
+
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+  static coroutine_handle from_address(void *) noexcept;
+  static coroutine_handle from_promise(Promise &promise);
+  constexpr void* address() const noexcept;
+};
+template <>
+struct coroutine_handle<void> {
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+  static coroutine_handle from_address(void *);
+  constexpr void* address() const noexcept;
+};
+
+struct suspend_always {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+struct suspend_never {
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+} // namespace std
+
+#endif // STD_COROUTINE_H

diff  --git a/clang/test/CodeGenCXX/type-aware-allocators.cpp b/clang/test/CodeGenCXX/type-aware-allocators.cpp
new file mode 100644
index 0000000000000..cce9197ed0d12
--- /dev/null
+++ b/clang/test/CodeGenCXX/type-aware-allocators.cpp
@@ -0,0 +1,212 @@
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx    -fsized-deallocation    -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_SIZED_ALIGNED  %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation    -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_ALIGNED %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_NO_ALIGN %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx    -fsized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_SIZED_NO_ALIGN %s
+// Test default behaviour with c++26
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx    -fsized-deallocation    -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o -  | FileCheck --check-prefixes=CHECK,CHECK_SIZED_ALIGNED  %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation    -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o -  | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_ALIGNED %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o -  | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_NO_ALIGN %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx    -fsized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o -  | FileCheck --check-prefixes=CHECK,CHECK_SIZED_NO_ALIGN %s
+
+
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t {};
+}
+
+using size_t = __SIZE_TYPE__;
+
+// Sanity check to esure the semantics of the selected compiler mode
+// will trigger the exception handlers we are expecting, before
+// involving type aware allocation.
+// We duplicate the struct definitions so we don't trigger diagnostics
+// for changing operator resolution on the same type, and we do the
+// untyped test before specifying the typed operators rather than using
+// template constraints so we don't have to deal with monstrous mangling.
+struct S1 {
+  S1();
+};
+
+struct __attribute__((aligned(128))) S2 {
+  S2();
+};
+
+struct S3 {
+  S3();
+};
+
+struct __attribute__((aligned(128))) S4 {
+  S4();
+  char buffer[130];
+};
+
+extern "C" void test_no_type_aware_allocator() {
+  S1 *s1 = new S1;
+  delete s1;
+  S2 *s2 = new S2;
+  delete s2;
+}
+// CHECK-LABEL: test_no_type_aware_allocator
+// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
+// CHECK: @_ZN2S1C1Ev({{.*}} [[ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S1LPAD:lpad]]
+// CHECK_SIZED_ALIGNED: @_ZdlPvm(
+// CHECK_SIZED_NO_ALIGN: @_ZdlPvm(
+// CHECK_NO_SIZE_ALIGNED: @_ZdlPv(
+// CHECK_NO_SIZE_NO_ALIGN: @_ZdlPv(
+// CHECK_SIZED_ALIGNED: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwmSt11align_val_t(
+// CHECK_NO_SIZE_ALIGNED: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwmSt11align_val_t(
+// CHECK_SIZED_NO_ALIGN: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
+// CHECK_NO_SIZE_NO_ALIGN: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
+// CHECK: _ZN2S2C1Ev({{.*}} [[ALIGNED_ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S2LPAD:lpad3]]
+// CHECK_SIZED_ALIGNED: _ZdlPvmSt11align_val_t(
+// CHECK_NO_SIZE_ALIGNED: _ZdlPvSt11align_val_t(
+// CHECK_SIZED_NO_ALIGN: _ZdlPvm(
+// CHECK_NO_SIZE_NO_ALIGN: _ZdlPv(
+// CHECK: [[S1LPAD]]:{{.*}};
+// CHECK_SIZED_ALIGNED: @_ZdlPvm({{.*}}[[ALLOC_RESULT]], {{.*}})
+// CHECK_SIZED_NO_ALIGN: @_ZdlPvm({{.*}}[[ALLOC_RESULT]], {{.*}})
+// CHECK_NO_SIZE_ALIGNED: @_ZdlPv({{.*}}[[ALLOC_RESULT]])
+// CHECK_NO_SIZE_NO_ALIGN: @_ZdlPv({{.*}}[[ALLOC_RESULT]])
+// CHECK: [[S2LPAD]]:
+// CHECK_SIZED_ALIGNED: _ZdlPvSt11align_val_t({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
+// CHECK_NO_SIZE_ALIGNED: _ZdlPvSt11align_val_t({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
+// CHECK_SIZED_NO_ALIGN: _ZdlPvm({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
+// CHECK_NO_SIZE_NO_ALIGN: _ZdlPv({{.*}}[[ALIGNED_ALLOC_RESULT]])
+
+template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
+
+extern "C" void test_free_type_aware_allocator() {
+  S3 *s3 = new S3;
+  delete s3;
+  S4 *s4 = new S4;
+  delete s4;
+}
+// CHECK-LABEL: test_free_type_aware_allocator
+// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwI2S3EPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S3C1Ev({{.*}}[[ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S3LPAD:.*]]
+// CHECK: @_ZdlI2S3EvSt13type_identityIT_EPvmSt11align_val_t(
+// CHECK: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwI2S4EPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S4C1Ev({{.*}}[[ALIGNED_ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S4LPAD:.*]]
+// CHECK: @_ZdlI2S4EvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}}, {{.*}} 256, {{.*}} 128)
+// CHECK: [[S3LPAD]]:
+// CHECK: @_ZdlI2S3EvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}}[[ALLOC_RESULT]], {{.*}} 1, {{.*}} 1)
+// CHECK: [[S4LPAD]]:
+// CHECK: @_ZdlI2S4EvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}} 256, {{.*}} 128)
+
+struct S5 {
+  S5();
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
+  void operator delete(S5*, std::destroying_delete_t);
+};
+
+extern "C" void test_ensure_type_aware_cleanup() {
+  S5 *s5 = new S5;
+  delete s5;
+}
+// CHECK-LABEL: test_ensure_type_aware_cleanup
+// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_ZN2S5nwIS_EEPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S5C1Ev({{.*}}[[ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S5LPAD:.*]]
+// CHECK: @_ZN2S5dlEPS_St19destroying_delete_t(
+// CHECK: [[S5LPAD]]:
+// CHECK: @_ZN2S5dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}} [[ALLOC_RESULT]]
+
+struct S6 {
+  S6();
+  virtual ~S6();
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
+};
+
+S6::~S6(){
+}
+// CHECK-LABEL: _ZN2S6D0Ev
+// CHECK: _ZN2S6dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t(
+
+struct __attribute__((aligned(128))) S7 : S6 {
+
+};
+
+
+struct S8 : S6 {
+  S8();
+  void *operator new(size_t);
+  void operator delete(void*);
+};
+
+S8::S8(){}
+
+extern "C" void test_ensure_type_aware_overrides() {
+  S6 *s6 = new S6;
+  delete s6;
+  S7 *s7 = new S7;
+  delete s7;
+  S8 *s8 = new S8;
+  delete s8;
+}
+// CHECK-LABEL: test_ensure_type_aware_overrides
+// CHECK: [[S6_ALLOC:%.*]] = call {{.*}} @_ZN2S6nwIS_EEPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S6C1Ev({{.*}}[[S6_ALLOC]])
+// CHECK-NEXT: unwind label %[[S6LPAD:.*]]
+// CHECK: [[S6_VTABLE:%vtable.*]] = load 
+// CHECK: [[S6_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S6_VTABLE]], i64 1
+// CHECK: [[S6_DFN:%.*]] = load ptr, ptr [[S6_DFN_ADDR]]
+// CHECK: call void [[S6_DFN]](
+// CHECK: [[S7_ALLOC:%.*]] = call {{.*}} @_ZN2S6nwI2S7EEPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S7C1Ev({{.*}}[[S7_ALLOC]])
+// CHECK-NEXT: unwind label %[[S7LPAD:.*]]
+// CHECK: [[S7_VTABLE:%vtable.*]] = load
+// CHECK: [[S7_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S7_VTABLE]], i64 1
+// CHECK: [[S7_DFN:%.*]] = load ptr, ptr [[S7_DFN_ADDR]]
+// CHECK: call void [[S7_DFN]](
+// CHECK: [[S8_ALLOC:%.*]] = call {{.*}} @_ZN2S8nwEm(
+// CHECK: @_ZN2S8C1Ev({{.*}}[[S8_ALLOC]])
+// CHECK-NEXT: unwind label %[[S8LPAD:.*]]
+// CHECK: [[S8_VTABLE:%vtable.*]] = load
+// CHECK: [[S8_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S8_VTABLE]], i64 1
+// CHECK: [[S8_DFN:%.*]] = load ptr, ptr [[S8_DFN_ADDR]]
+// CHECK: call void [[S8_DFN]](
+// CHECK: [[S6LPAD]]:
+// CHECK: @_ZN2S6dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}} [[S6_ALLOC]], {{.*}}, {{.*}})
+// CHECK: [[S7LPAD]]:
+// CHECK: @_ZN2S6dlI2S7EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}} [[S7_ALLOC]], {{.*}}, {{.*}})
+// CHECK: [[S8LPAD]]:
+// CHECK: @_ZN2S8dlEPv({{.*}} [[S8_ALLOC]])
+
+struct __attribute__((aligned(128))) S11 {
+  S11();
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
+  void operator delete(S11*, std::destroying_delete_t, std::align_val_t);
+};
+
+
+struct S12 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, unsigned line = __builtin_LINE());
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, unsigned line = __builtin_LINE());
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
+};
+
+extern "C" void test_ensure_type_aware_resolution_includes_location() {
+  S12 *s12 = new S12(); // test line
+  delete s12;
+}
+
+// CHECK-LABEL: test_ensure_type_aware_resolution_includes_location
+// `180` in the next line is the line number from the test line in above
+// CHECK: %call = call noundef ptr @_ZN3S12nwIS_EEPvSt13type_identityIT_EmSt11align_val_tj({{.*}}, {{.*}}, {{.*}}, {{.*}})
+
+// CHECK-LABEL: @_ZN2S8D0Ev
+// CHECK: @_ZN2S8dlEPv(
+
+// CHECK-LABEL: _ZN2S7D0Ev
+// CHECK: _ZN2S6dlI2S7EEvSt13type_identityIT_EPvmSt11align_val_t(

diff  --git a/clang/test/CodeGenCXX/type-aware-coroutines.cpp b/clang/test/CodeGenCXX/type-aware-coroutines.cpp
new file mode 100644
index 0000000000000..0a19079d987e9
--- /dev/null
+++ b/clang/test/CodeGenCXX/type-aware-coroutines.cpp
@@ -0,0 +1,128 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx  %s -std=c++23 -fcoroutines -fexceptions -emit-llvm  -Wno-coro-type-aware-allocation-function -o - | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-macosx  %s -std=c++26 -fcoroutines -fexceptions -emit-llvm  -Wno-coro-type-aware-allocation-function -o - | FileCheck %s
+
+#include "Inputs/std-coroutine.h"
+
+namespace std {
+   template <typename T> struct type_identity {
+   typedef T type;
+   };
+   typedef __SIZE_TYPE__ size_t;
+   enum class align_val_t {};
+}
+
+struct Allocator {};
+
+struct resumable {
+  struct promise_type {
+    promise_type();
+    void *operator new(std::size_t sz, int);
+    void *operator new(std::size_t sz, float);
+    void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, int);
+    void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float);
+    void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t);
+    template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t) = delete;
+    void operator delete(void *);
+
+    resumable get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+
+struct resumable2 {
+  struct promise_type {
+    promise_type();
+    template <typename... Args> void *operator new(std::size_t sz, Args...);
+    template <typename... Args> void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, Args...);
+    void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t);
+    void operator delete(void *);
+
+    resumable2 get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+
+// CHECK-LABEL: void @f1
+extern "C" resumable f1(int) {
+  co_return;
+// CHECK: coro.alloc:
+// CHECK: _ZN9resumable12promise_typenwEmi
+// CHECK: coro.free:
+// CHECK: _ZN9resumable12promise_typedlEPv
+}
+
+// CHECK-LABEL: void @f2
+extern "C" resumable f2(float) {
+  co_return;
+// CHECK: coro.alloc:
+// CHECK: _ZN9resumable12promise_typenwEmf
+// CHECK: coro.free:
+// CHECK: _ZN9resumable12promise_typedlEPv
+}
+
+// CHECK-LABEL: void @f3
+extern "C" resumable2 f3(int, float, const char*, Allocator) {
+   co_yield 1;
+   co_return;
+// CHECK: coro.alloc:
+// CHECK: _ZN10resumable212promise_typenwIJifPKc9AllocatorEEEPvmDpT_
+// CHECK: coro.free:
+// CHECK: _ZN10resumable212promise_typedlEPv
+// CHECK: _ZN10resumable212promise_typedlEPv
+}
+
+// CHECK-LABEL: void @f4
+extern "C" resumable f4(int n = 10) {
+   for (int i = 0; i < n; i++) co_yield i;
+// CHECK: coro.alloc:
+// CHECK: call {{.*}}@_ZN9resumable12promise_typenwEmi(
+// CHECK: coro.free:
+// CHECK: call void @_ZN9resumable12promise_typedlEPv(
+// CHECK: call void @_ZN9resumable12promise_typedlEPv(
+}
+
+struct resumable3 {
+  struct promise_type {
+    promise_type();
+    resumable3 get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+template <typename T> void *operator new(std::type_identity<T>, std::size_t sz, std::align_val_t);
+template <typename T, typename... Args> void *operator new(std::type_identity<T>, std::size_t sz, std::align_val_t, Args...);
+template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t);
+template <typename T, typename... Args> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t, Args...);
+
+// CHECK-LABEL: void @f5
+extern "C" resumable3 f5(float) {
+  co_return;
+// CHECK: coro.alloc:
+// CHECK: call {{.*}}@_Znwm(
+// CHECK: coro.free:
+// CHECK: call void @_ZdlPvm(
+// CHECK: call void @_ZdlPvm(
+}
+
+// CHECK-LABEL: void @f4.resume
+// CHECK: coro.free:
+// CHECK: _ZN9resumable12promise_typedlEPv
+
+// CHECK-LABEL: void @f4.destroy
+// CHECK: coro.free:
+// CHECK: _ZN9resumable12promise_typedlEPv
+
+// CHECK-LABEL: void @f4.cleanup
+// CHECK: coro.free:
+// CHECK: _ZN9resumable12promise_typedlEPv

diff  --git a/clang/test/CodeGenCXX/type-aware-new-constexpr.cpp b/clang/test/CodeGenCXX/type-aware-new-constexpr.cpp
new file mode 100644
index 0000000000000..57eba8c7cb7f0
--- /dev/null
+++ b/clang/test/CodeGenCXX/type-aware-new-constexpr.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx     -fsized-deallocation    -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx  -fno-sized-deallocation    -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx  -fno-sized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx     -fsized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -faligned-allocation                             -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -fexperimental-new-constant-interpreter -o - | FileCheck %s
+
+using size_t = __SIZE_TYPE__;
+
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : size_t {};
+}
+
+
+template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
+struct S {
+  int i = 0;
+  constexpr S() __attribute__((noinline)) {}
+};
+
+ constexpr int doSomething() {
+  S* s = new S;
+  int result = s->i;
+  delete s;
+  return result;
+}
+
+static constexpr int force_doSomething = doSomething();
+template <int N> struct Tag {};
+
+void test1(Tag<force_doSomething>){
+// CHECK-LABEL: define void @_Z5test13TagILi0EE
+}
+
+void test2(Tag<doSomething() + 1>){
+// CHECK-LABEL: define void @_Z5test23TagILi1EE
+}
+
+int main() {
+  // CHECK-LABEL: define noundef i32 @main()
+  return doSomething();
+  // CHECK: call{{.*}}i32 @_Z11doSomethingv()
+}
+
+// CHECK-LABEL: define linkonce_odr noundef i32 @_Z11doSomethingv()
+// CHECK: [[ALLOC:%.*]] = call noundef ptr @_ZnwI1SEPvSt13type_identityIT_Em
+// CHECK: invoke noundef ptr @_ZN1SC1Ev(ptr{{.*}} [[ALLOC]])
+// CHECK-NEXT: to label %[[CONT:.*]] unwind label %[[LPAD:[a-z0-9]+]]
+// CHECK: [[CONT]]:
+// CHECK: call void @_ZdlI1SEvSt13type_identityIT_EPv
+// CHECK: ret
+// CHECK: [[LPAD]]:
+// call void @_ZdlI1SEvSt13type_identityIT_EPvmSt11align_val_t({{.*}} [[ALLOC]])

diff  --git a/clang/test/CodeGenCXX/type-aware-placement-operators.cpp b/clang/test/CodeGenCXX/type-aware-placement-operators.cpp
new file mode 100644
index 0000000000000..858db62ffcfb0
--- /dev/null
+++ b/clang/test/CodeGenCXX/type-aware-placement-operators.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23    -fsized-deallocation    -faligned-allocation -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -fno-sized-deallocation    -faligned-allocation -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23    -fsized-deallocation -fno-aligned-allocation -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -fno-aligned-allocation -fno-sized-deallocation -o - | FileCheck %s
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+}
+
+using size_t = __SIZE_TYPE__;
+struct Context;
+struct S1 {
+  S1();
+  int i;
+};
+
+void *operator new(std::type_identity<S1>, size_t, std::align_val_t, Context&);
+void operator delete(std::type_identity<S1>, void*, size_t, std::align_val_t, Context&); // #1
+void operator delete(std::type_identity<S1>, void*, size_t, std::align_val_t);
+
+struct S2 {
+  S2();
+  int i;
+  template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&);
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Context&); // #2
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #3
+};
+
+struct S3 {
+  S3();
+  int i;
+  template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #4
+};
+
+extern "C" void test_s1(Context& Ctx) {
+  S1 *s1 = new (Ctx) S1;
+  delete s1;
+}
+
+// CHECK-LABEL: test_s1
+// CHECK: [[S1_NEW:%.*]] = call noundef ptr @_ZnwSt13type_identityI2S1EmSt11align_val_tR7Context({{.*}},{{.*}} 4,{{.*}} 4, ptr noundef nonnull align 1 [[S1_NEW_CONTEXT:%.*]])
+// CHECK: call void @_ZdlSt13type_identityI2S1EPvmSt11align_val_t({{.*}}, ptr noundef %2,{{.*}} 4,{{.*}} 4)
+// CHECK: call void @_ZdlSt13type_identityI2S1EPvmSt11align_val_tR7Context({{.*}}, ptr noundef [[S1_NEW]],{{.*}} 4,{{.*}} 4, ptr noundef nonnull align 1 [[S1_NEW_CONTEXT]])
+
+extern "C" void test_s2(Context& Ctx) {
+  S2 *s2_1 = new (Ctx) S2;
+  delete s2_1;
+  S2 *s2_2 = new (std::align_val_t(128), Ctx) S2;
+  delete s2_2;
+}
+
+// CHECK-LABEL: test_s2
+// CHECK: [[S2_NEW1:%.*]] = call noundef ptr @_ZN2S2nwIS_EEPvSt13type_identityIT_EmSt11align_val_tR7Context({{.*}},{{.*}} 4,{{.*}} 4,{{.*}} [[S2_NEW1_CONTEXT:%.*]])
+// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
+// CHECK: [[S2_NEW2:%.*]] = call noundef ptr @_ZN2S2nwIS_EEPvSt13type_identityIT_EmSt11align_val_tR7Context({{.*}},{{.*}} 4,{{.*}} 128,{{.*}} [[S2_NEW2_CONTEXT:%.*]])
+// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
+// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_tR7Context({{.*}}, {{.*}} [[S2_NEW1]],{{.*}} 4,{{.*}} 4,{{.*}} [[S2_NEW1_CONTEXT]])
+// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_tR7Context({{.*}}, {{.*}} [[S2_NEW2]],{{.*}} 4,{{.*}} 128,{{.*}} [[S2_NEW2_CONTEXT]])
+
+extern "C" void test_s3(Context& Ctx) {
+  S3 *s3_1 = new S3;
+  delete s3_1;
+  S3 *s3_2 = new (std::align_val_t(128)) S3;
+  delete s3_2;
+}
+
+// CHECK-LABEL: test_s3
+// CHECK: [[S3_NEW1:%.*]] = call noundef ptr @_ZN2S3nwIS_EEPvSt13type_identityIT_EmSt11align_val_t({{.*}},{{.*}} 4,{{.*}} 4)
+// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4) 
+// CHECK: [[S3_NEW2:%.*]] = call noundef ptr @_ZN2S3nwIS_EEPvSt13type_identityIT_EmSt11align_val_t({{.*}},{{.*}} 4,{{.*}} 128)
+// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
+// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}}[[S3_NEW1]],{{.*}} 4,{{.*}} 4)
+// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}}[[S3_NEW2]],{{.*}} 4,{{.*}} 128)

diff  --git a/clang/test/CodeGenCoroutines/coro-alloc-2.cpp b/clang/test/CodeGenCoroutines/coro-alloc-2.cpp
index 9c60c32a5c544..460f862deaba8 100644
--- a/clang/test/CodeGenCoroutines/coro-alloc-2.cpp
+++ b/clang/test/CodeGenCoroutines/coro-alloc-2.cpp
@@ -1,5 +1,7 @@
 // Tests that we wouldn't generate an allocation call in global scope with (std::size_t, p0, ..., pn)
 // RUN: %clang_cc1 %s -std=c++20 -triple x86_64 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
+// RUN: %clang_cc1 %s -std=c++26 -triple x86_64 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
+
 #include "Inputs/coroutine.h"
 
 namespace std {

diff  --git a/clang/test/Modules/new-delete.cpp b/clang/test/Modules/new-delete.cpp
index 585a242b22474..438b727386959 100644
--- a/clang/test/Modules/new-delete.cpp
+++ b/clang/test/Modules/new-delete.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fmodules -verify %s
+// RUN: %clang_cc1 -fmodules -std=c++26 -verify %s
 // expected-no-diagnostics
 
 #pragma clang module build M

diff  --git a/clang/test/SemaCXX/cxx2a-destroying-delete.cpp b/clang/test/SemaCXX/cxx2a-destroying-delete.cpp
index bf0a64a77385c..27ea6663bae6a 100644
--- a/clang/test/SemaCXX/cxx2a-destroying-delete.cpp
+++ b/clang/test/SemaCXX/cxx2a-destroying-delete.cpp
@@ -16,7 +16,7 @@ namespace std {
 void operator delete(void*, std::destroying_delete_t); // ok, just a placement delete
 
 struct A;
-void operator delete(A*, std::destroying_delete_t); // expected-error {{first parameter of 'operator delete' must have type 'void *'}}
+void operator delete(A*, std::destroying_delete_t); // expected-error {{1st parameter of 'operator delete' must have type 'void *'}}
 
 struct A {
   void operator delete(A*, std::destroying_delete_t);
@@ -27,7 +27,7 @@ struct A {
   // FIXME: It's probably a language defect that we permit usual operator delete to be variadic.
   void operator delete(A*, std::destroying_delete_t, std::size_t, ...);
 
-  void operator delete(struct X*, std::destroying_delete_t, std::size_t, ...); // expected-error {{first parameter of 'operator delete' must have type 'A *'}}
+  void operator delete(struct X*, std::destroying_delete_t, std::size_t, ...); // expected-error {{1st parameter of destroying 'operator delete' must have type 'A *'}}
 
   void operator delete(void*, std::size_t);
 };
@@ -191,7 +191,7 @@ namespace delete_from_new {
 namespace GH96191 {
   struct S {};
   struct T {
-    void operator delete(S) { } // expected-error {{first parameter of 'operator delete' must have type 'void *'}}
+    void operator delete(S) { } // expected-error {{1st parameter of 'operator delete' must have type 'void *'}}
   };
 
   void foo(T *t) { delete t; }

diff  --git a/clang/test/SemaCXX/delete.cpp b/clang/test/SemaCXX/delete.cpp
index 7d1f51cb218ce..f97b45de52147 100644
--- a/clang/test/SemaCXX/delete.cpp
+++ b/clang/test/SemaCXX/delete.cpp
@@ -5,6 +5,10 @@
 // RUN: %clang_cc1 -x c++-header -std=c++11 -emit-pch -o %t %S/delete-mismatch.h
 // RUN: %clang_cc1 -std=c++11 -include-pch %t -DWITH_PCH -verify %s -ast-dump
 
+// Test with PCH and type aware allocators
+// RUN: %clang_cc1 -x c++-header -std=c++11 -emit-pch -o %t.taa.pch %S/delete-mismatch.h
+// RUN: %clang_cc1 -std=c++11 -include-pch %t.taa.pch -DWITH_PCH -verify %s -ast-dump
+
 void f(int a[10][20]) {
   delete a; // expected-warning {{'delete' applied to a pointer-to-array type}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]"

diff  --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp
index fb4810ad673ad..9bbee32c58c36 100644
--- a/clang/test/SemaCXX/new-delete.cpp
+++ b/clang/test/SemaCXX/new-delete.cpp
@@ -251,7 +251,7 @@ void loadEngineFor() {
 }
 
 template <class T> struct TBase {
-  void* operator new(T size, int); // expected-error {{'operator new' cannot take a dependent type as first parameter; use size_t}}
+  void* operator new(T size, int); // expected-error {{'operator new' cannot take a dependent type as its 1st parameter; use size_t}}
 };
 
 TBase<int> t1;
@@ -466,7 +466,7 @@ namespace TemplateDestructors {
 
 namespace DeleteParam {
   struct X {
-    void operator delete(X*); // expected-error{{first parameter of 'operator delete' must have type 'void *'}}
+    void operator delete(X*); // expected-error{{1st parameter of 'operator delete' must have type 'void *'}}
   };
 
   struct Y {

diff  --git a/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp b/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp
new file mode 100644
index 0000000000000..57e6d953c2ad6
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp
@@ -0,0 +1,261 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions    -fsized-deallocation    -faligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=1
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation    -faligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=0
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions    -fsized-deallocation -fno-aligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=1
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation -fno-aligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=0
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+}
+
+using size_t = __SIZE_TYPE__;
+
+void *operator new(size_t); // #default_operator_new
+
+#if DEFAULT_DELETE==0
+void operator delete(void*) noexcept; // #default_operator_delete
+#elif DEFAULT_DELETE==1
+void operator delete(void*, size_t) noexcept; // #default_operator_delete
+#elif DEFAULT_DELETE==2
+void operator delete(void*, std::align_val_t) noexcept; // #default_operator_delete
+#elif DEFAULT_DELETE==3
+void operator delete(void*, size_t, std::align_val_t) noexcept; // #default_operator_delete
+#endif
+
+struct Invalid1 {
+  // expected-error at -1 {{declaration of type aware 'operator new' in 'Invalid1' must have matching type aware 'operator delete'}}
+  void *operator new(std::type_identity<Invalid1>, size_t, std::align_val_t);
+  // expected-note at -1 {{unmatched type aware 'operator new' declared here}}
+};
+struct Invalid2 {
+  // expected-error at -1 {{declaration of type aware 'operator new' in 'Invalid2' must have matching type aware 'operator delete'}}
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #Invalid2_new
+  // expected-note at -1 {{unmatched type aware 'operator new' declared here}}
+};
+struct Invalid3 {
+  // expected-error at -1 {{declaration of type aware 'operator delete' in 'Invalid3' must have matching type aware 'operator new'}}
+  void operator delete(std::type_identity<Invalid3>, void*, size_t, std::align_val_t);
+  // expected-note at -1 {{unmatched type aware 'operator delete' declared here}}
+};
+struct Invalid4 {
+  // expected-error at -1 {{declaration of type aware 'operator delete' in 'Invalid4' must have matching type aware 'operator new'}}
+  template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #Invalid4_delete
+  // expected-note at -1 {{unmatched type aware 'operator delete' declared here}}
+};
+struct Invalid5 {
+  // expected-error at -1 {{declaration of type aware 'operator new[]' in 'Invalid5' must have matching type aware 'operator delete[]'}}
+  void *operator new[](std::type_identity<Invalid5>, size_t, std::align_val_t);
+  // expected-note at -1 {{unmatched type aware 'operator new[]' declared here}}
+};
+struct Invalid6 {
+  // expected-error at -1 {{declaration of type aware 'operator new[]' in 'Invalid6' must have matching type aware 'operator delete[]'}}
+  template <class T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t);
+  // expected-note at -1 {{unmatched type aware 'operator new[]' declared here}}
+};
+struct Invalid7 {
+  // expected-error at -1 {{declaration of type aware 'operator delete[]' in 'Invalid7' must have matching type aware 'operator new[]'}}
+  void operator delete[](std::type_identity<Invalid7>, void*, size_t, std::align_val_t);
+  // expected-note at -1 {{unmatched type aware 'operator delete[]' declared here}}
+};
+struct Invalid8 {
+  // expected-error at -1 {{declaration of type aware 'operator delete[]' in 'Invalid8' must have matching type aware 'operator new[]'}}
+  template <class T> void operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
+  // expected-note at -1 {{unmatched type aware 'operator delete[]' declared here}}
+};
+
+// Invalid9 and Invalid10 will ensure we report the correct owner for the
+// resolved, but unmatched, new and delete
+struct Invalid9: Invalid2 {};
+struct Invalid10: Invalid4 {};
+// Invalid11 inherits a "matching" new and delete pair (so no inheritance ambiguity)
+// but the resolved operators are from 
diff erent scopes
+struct Invalid11 : Invalid2, Invalid4 {};
+struct Invalid12 : Invalid2, Invalid4 {
+  using Invalid2::operator new;
+  using Invalid4::operator delete;
+};
+
+struct TestClass1 {
+  void *operator new(std::type_identity<TestClass1>, size_t, std::align_val_t); // #TestClass1_new
+  void  operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #TestClass1_delete
+};
+
+struct TestClass2 {
+  void *operator new(std::type_identity<int>, size_t, std::align_val_t); // #TestClass2_new
+  void  operator delete(std::type_identity<TestClass2>, void *, size_t, std::align_val_t);  // #TestClass2_delete
+};
+
+void basic_tests() {
+  TestClass1 * tc1 = new TestClass1;
+  delete tc1;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'TestClass1'}}
+  // expected-note@#TestClass1_delete {{member 'operator delete' declared here}}
+  TestClass2 * tc2 = new TestClass2;
+  // expected-error at -1 {{no matching function for call to 'operator new'}}
+  delete tc2;
+  Invalid9 * i9 = new Invalid9;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
+  delete i9;
+  Invalid10 * i10 = new Invalid10;
+  // expected-error at -1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
+  // expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
+  // expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
+  delete i10;
+  Invalid11 * i11 = new Invalid11;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
+  // expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
+  delete i11;
+  Invalid12 * i12 = new Invalid12;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
+  // expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
+  delete i12;
+}
+
+struct Baseclass1 {
+  void *operator new(std::type_identity<Baseclass1>, size_t, std::align_val_t);
+  void  operator delete(std::type_identity<Baseclass1>, void *, size_t, std::align_val_t); // #Baseclass1_delete
+};
+
+struct Subclass1 : Baseclass1 {
+  Subclass1();
+};
+
+struct Baseclass2 {
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  void  operator delete(std::type_identity<Baseclass2>, void *, size_t, std::align_val_t); // #Baseclass2_delete
+};
+
+struct Subclass2 : Baseclass2 {
+  Subclass2();
+};
+
+struct Baseclass3 {
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template <class T> void  operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
+};
+
+struct Subclass3_1 : Baseclass3 {
+  Subclass3_1();
+  void *operator new(std::type_identity<int>, size_t, std::align_val_t);
+  template <class T> void  operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
+};
+
+struct Subclass3_2 : Baseclass3 {
+  Subclass3_2();
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  void  operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #Subclass3_2_delete
+};
+
+
+void test_subclasses() {
+  Subclass1 * sc1 = new Subclass1;
+  // expected-error at -1 {{no matching function for call to 'operator new'}}
+  delete sc1;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'Subclass1'}}
+  // expected-note@#Baseclass1_delete {{member 'operator delete' declared here}}
+  Subclass2 * sc2 = new Subclass2;
+  delete sc2;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'Subclass2'}}
+  // expected-note@#Baseclass2_delete {{member 'operator delete' declared here}}
+  Subclass3_1 * sc3_1 = new Subclass3_1;
+  // expected-error at -1 {{no matching function for call to 'operator new'}}
+  delete sc3_1;
+  Subclass3_2 * sc3_2 = new Subclass3_2;
+  delete sc3_2;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'Subclass3_2'}}
+  // expected-note@#Subclass3_2_delete {{member 'operator delete' declared here}}
+}
+
+template <class A, class B> constexpr bool same_type_v = false;
+template <class A> constexpr bool same_type_v<A, A> = true;
+
+template <class T> struct InvalidConstrainedOperator {
+  template <class U> void *operator new(std::type_identity<U>, size_t, std::align_val_t);
+  template <class U> requires (same_type_v<T, int>) void  operator delete(std::type_identity<U>, void *, size_t, std::align_val_t); // #InvalidConstrainedOperator_delete
+};
+
+struct Context;
+template <class T> struct InvalidConstrainedCleanup {
+  template <class U> void *operator new(std::type_identity<U>, size_t, std::align_val_t, Context&); // #InvalidConstrainedCleanup_placement_new
+  template <class U> requires (same_type_v<T, int>) void operator delete(std::type_identity<U>, void *, size_t, std::align_val_t, Context&); // #InvalidConstrainedCleanup_delete
+  template <class U> void operator delete(std::type_identity<U>, void *, size_t, std::align_val_t);
+};
+
+void test_incompatible_constrained_operators(Context &Ctx) {
+  InvalidConstrainedOperator<int> *ico1 = new InvalidConstrainedOperator<int>;
+  delete ico1;
+  InvalidConstrainedOperator<float> *ico2 = new InvalidConstrainedOperator<float>;
+  delete ico2;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'InvalidConstrainedOperator<float>'}}
+  // expected-note@#InvalidConstrainedOperator_delete {{member 'operator delete' declared here}}
+  InvalidConstrainedCleanup<int> *icc1 = new (Ctx) InvalidConstrainedCleanup<int>;
+  delete icc1;
+  InvalidConstrainedCleanup<float> *icc2 = new (Ctx) InvalidConstrainedCleanup<float>;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
+  // expected-note@#InvalidConstrainedCleanup_placement_new {{type aware 'operator new' declared here in 'InvalidConstrainedCleanup<float>'}}
+  delete icc2;
+}
+
+typedef struct {
+  // expected-error at -1 {{declaration of type aware 'operator new' in '(unnamed struct}}
+  // expected-note@#AnonymousClass1_new {{unmatched type aware 'operator new' declared here}}
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #AnonymousClass1_new
+} AnonymousClass1;
+
+typedef struct {
+  // expected-error at -1 {{declaration of type aware 'operator delete' in '(unnamed struct}}
+  template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #AnonymousClass2_delete
+} AnonymousClass2;
+
+typedef struct {
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
+} AnonymousClass3;
+
+using AnonymousClass4 = struct {};
+using AnonymousClass5 = struct {};
+using AnonymousClass6 = struct {};
+using AnonymousClass7 = struct {
+  // expected-error at -1 {{declaration of type aware 'operator new' in}}
+  // expected-note@#AnonymousClass7_new {{unmatched type aware 'operator new' declared here}}
+  template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&); // #AnonymousClass7_new
+};
+
+
+void *operator new(std::type_identity<AnonymousClass4>, size_t, std::align_val_t); // #AnonymousClass4_new
+void operator delete(std::type_identity<AnonymousClass5>, void*, size_t, std::align_val_t); // #AnonymousClass5_delete
+void *operator new(std::type_identity<AnonymousClass6>, size_t, std::align_val_t, Context&); // #AnonymousClass6_placement_new
+void operator delete(std::type_identity<AnonymousClass6>, void*, size_t, std::align_val_t);
+void test_anonymous_types(Context &Ctx) {
+  AnonymousClass1 *ac1 = new AnonymousClass1;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#AnonymousClass1_new {{type aware 'operator new' declared here in 'AnonymousClass1'}}
+
+  delete ac1;
+  AnonymousClass2 *ac2 = new AnonymousClass2;
+  // expected-error at -1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
+  // expected-note@#AnonymousClass2_delete {{unmatched type aware 'operator delete' declared here}}
+  // expected-note@#AnonymousClass2_delete {{type aware 'operator delete' declared here in 'AnonymousClass2'}}
+  // expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
+
+  delete ac2;
+  AnonymousClass3 *ac3 = new AnonymousClass3;
+  delete ac3;
+  AnonymousClass4 *ac4 = new AnonymousClass4;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#AnonymousClass4_new {{type aware 'operator new' declared here in the global namespace}}
+  delete ac4;
+  AnonymousClass5 *ac5 = new AnonymousClass5;
+  // expected-error at -1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
+  // expected-note@#AnonymousClass5_delete {{type aware 'operator delete' declared here}}
+  // expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
+  delete ac5;
+  AnonymousClass6 *ac6 = new (Ctx) AnonymousClass6;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
+  // expected-note@#AnonymousClass6_placement_new {{type aware 'operator new' declared here in the global namespace}}
+  // expected-note@#default_operator_delete {{non-type aware 'operator delete' declared here in the global namespace}}
+  delete ac6;
+}
\ No newline at end of file

diff  --git a/clang/test/SemaCXX/type-aware-coroutines.cpp b/clang/test/SemaCXX/type-aware-coroutines.cpp
new file mode 100644
index 0000000000000..a54d37c47dbd9
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-coroutines.cpp
@@ -0,0 +1,145 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fcoroutines -fexceptions -Wall -Wpedantic
+
+
+#include "Inputs/std-coroutine.h"
+
+namespace std {
+   template <typename T> struct type_identity {
+   typedef T type;
+   };
+   typedef __SIZE_TYPE__ size_t;
+   enum class align_val_t : size_t {};
+}
+
+struct Allocator {};
+
+struct resumable {
+  struct promise_type {
+    void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, int); // #resumable_tan1
+    void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float); // #resumable_tan2
+    void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t); // #resumable_tad1
+    template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t) = delete; // #resumable_tad2
+
+    resumable get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+
+struct resumable2 {
+  struct promise_type {
+    template <typename... Args> void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, Args...); // #resumable2_tan1
+    void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t); // #resumable2_tad2
+
+    resumable2 get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+
+
+struct resumable3 {
+  struct promise_type {
+  // expected-error at -1 {{declaration of type aware 'operator new' in 'resumable3::promise_type' must have matching type aware 'operator delete'}}
+  // expected-note@#resumable3_tan {{unmatched type aware 'operator new' declared here}}
+    void *operator new(std::size_t sz, float);
+    void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float); // #resumable3_tan
+    void operator delete(void *);
+
+    resumable3 get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+struct resumable4 {
+  struct promise_type {
+    // expected-error at -1 {{declaration of type aware 'operator delete' in 'resumable4::promise_type' must have matching type aware 'operator new'}}
+    // expected-note@#resumable4_tad {{unmatched type aware 'operator delete' declared here}}
+    void *operator new(std::size_t sz, float);
+    template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable4_tad
+
+    resumable4 get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+struct resumable5 {
+  struct promise_type {
+    // expected-error at -1 {{declaration of type aware 'operator delete' in 'resumable5::promise_type' must have matching type aware 'operator new'}}
+    // expected-note@#resumable5_tad {{unmatched type aware 'operator delete' declared here}}
+    void *operator new(std::size_t sz, float);
+    void operator delete(void *);
+    template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable5_tad
+
+    resumable5 get_return_object() { return {}; }
+    auto initial_suspend() { return std::suspend_always(); }
+    auto final_suspend() noexcept { return std::suspend_always(); }
+    void unhandled_exception() {}
+    void return_void(){};
+    std::suspend_always yield_value(int i);
+  };
+};
+
+resumable f1(int) {
+  // expected-error at -1 {{'operator new' provided by 'std::coroutine_traits<resumable, int>::promise_type' (aka 'resumable::promise_type') is not usable with the function signature of 'f1'}}
+  // expected-note at -2 {{type aware 'operator new' will not be used for coroutine allocation}}
+  // expected-note@#resumable_tan1 {{type aware 'operator new' declared here}}
+  // expected-note@#resumable_tan2 {{type aware 'operator new' declared here}}
+  co_return;
+}
+
+resumable f2(float) {
+  // expected-error at -1 {{'operator new' provided by 'std::coroutine_traits<resumable, float>::promise_type' (aka 'resumable::promise_type') is not usable with the function signature of 'f2'}}
+  // expected-note at -2 {{type aware 'operator new' will not be used for coroutine allocation}}
+  // expected-note@#resumable_tan1 {{type aware 'operator new' declared here}}
+  // expected-note@#resumable_tan2 {{type aware 'operator new' declared here}}
+  co_return;
+}
+
+resumable2 f3(int, float, const char*, Allocator) {
+  // expected-error at -1 {{'operator new' provided by 'std::coroutine_traits<resumable2, int, float, const char *, Allocator>::promise_type' (aka 'resumable2::promise_type') is not usable with the function signature of 'f3'}}
+  // expected-note at -2 {{type aware 'operator new' will not be used for coroutine allocation}}
+  // expected-note@#resumable2_tan1 {{type aware 'operator new' declared here}}
+  co_yield 1;
+  co_return;
+}
+
+resumable f4(int n = 10) {
+  // expected-error at -1 {{'operator new' provided by 'std::coroutine_traits<resumable, int>::promise_type' (aka 'resumable::promise_type') is not usable with the function signature of 'f4'}}
+  // expected-note at -2 {{type aware 'operator new' will not be used for coroutine allocation}}
+  // expected-note@#resumable_tan1 {{type aware 'operator new' declared here}}
+  // expected-note@#resumable_tan2 {{type aware 'operator new' declared here}}
+  for (int i = 0; i < n; i++)
+    co_yield i;
+}
+resumable3 f5(float) {
+  // expected-warning at -1 {{type aware 'operator new' will not be used for coroutine allocation}}
+  // expected-note@#resumable3_tan {{type aware 'operator new' declared here}}
+  co_return;
+}
+
+resumable4 f6(float) {
+  // expected-error at -1 {{no suitable member 'operator delete' in 'promise_type'}}
+  // expected-warning at -2 {{type aware 'operator delete' will not be used for coroutine allocation}}
+  // expected-note@#resumable4_tad {{type aware 'operator delete' declared here}}
+  // expected-note@#resumable4_tad {{member 'operator delete' declared here}}
+  co_return;
+}
+
+resumable5 f7(float) {
+  // expected-warning at -1 {{type aware 'operator delete' will not be used for coroutine allocation}}
+  // expected-note@#resumable5_tad {{type aware 'operator delete' declared here}}
+  co_return;
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-constexpr.cpp b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
new file mode 100644
index 0000000000000..105610cd103e6
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions    -fsized-deallocation    -faligned-allocation 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation    -faligned-allocation 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions    -fsized-deallocation -fno-aligned-allocation 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions    -fsized-deallocation    -faligned-allocation -fexperimental-new-constant-interpreter 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation    -faligned-allocation -fexperimental-new-constant-interpreter 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions    -fsized-deallocation -fno-aligned-allocation -fexperimental-new-constant-interpreter 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation -fexperimental-new-constant-interpreter 
+
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+
+struct S1 {
+  constexpr explicit S1() : i(5) {  }
+  const int i;
+};
+
+void *operator new(std::type_identity<S1>, size_t sz, std::align_val_t); // #1
+void operator delete(std::type_identity<S1>, void* ptr, size_t sz, std::align_val_t); // #2
+
+constexpr int ensure_consteval_skips_typed_allocators() {
+  // Verify we dont resolve typed allocators in const contexts
+  auto * s = new S1();
+  auto result = s->i;
+  delete s;
+  return result;
+};
+
+struct S2 {
+  constexpr explicit S2() : i(5) {  }
+  const int i;
+};
+
+void *operator new(std::type_identity<S2>, size_t sz, std::align_val_t) = delete; // #3
+void operator delete(std::type_identity<S2>, void* ptr, size_t sz, std::align_val_t) = delete; // #4
+
+constexpr int ensure_constexpr_retains_types_at_runtime() {
+  // Verify we dont resolve typed allocators in const contexts
+  S2 *s = new S2();
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#1 {{candidate function not viable: no known conversion from 'type_identity<S2>' to 'type_identity<S1>' for 1st argument}}
+  // expected-note@#3 {{candidate function has been explicitly deleted}}
+  auto result = s->i;
+  delete s;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#4 {{'operator delete' has been explicitly marked deleted here}}
+  return result;
+};
+
+
+struct S3 {
+  constexpr explicit S3() : i(5) {  }
+  const int i;
+  template <typename T> void* operator new(std::type_identity<T>, size_t sz, std::align_val_t) = delete; // #5
+  template <typename T> void operator delete(std::type_identity<T>, void *, size_t sz, std::align_val_t) = delete; // #6
+};
+
+template <typename T> void* operator new(std::type_identity<T>, size_t sz, std::align_val_t) = delete; // #7
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t sz, std::align_val_t) = delete; // #8
+
+constexpr int constexpr_vs_inclass_operators() {
+  S3 *s;
+  if consteval {
+    s = ::new S3();
+    // expected-error at -1 {{call to deleted function 'operator new'}}
+    // expected-note@#1 {{candidate function not viable: no known conversion from 'type_identity<S3>' to 'type_identity<S1>' for 1st argument}}
+    // expected-note@#3 {{candidate function not viable: no known conversion from 'type_identity<S3>' to 'type_identity<S2>' for 1st argument}}
+    // expected-note@#7 {{candidate function [with T = S3] has been explicitly deleted}}
+  } else {
+    s = new S3();
+    // expected-error at -1 {{call to deleted function 'operator new'}}
+    // expected-note@#5 {{candidate function [with T = S3] has been explicitly deleted}}
+  }
+  auto result = s->i;
+  if consteval {
+    ::delete s;
+    // expected-error at -1 {{attempt to use a deleted function}}
+    // expected-note@#8 {{'operator delete<S3>' has been explicitly marked deleted here}}
+  } else {
+    delete s;
+    // expected-error at -1 {{attempt to use a deleted function}}
+    // expected-note@#6 {{'operator delete<S3>' has been explicitly marked deleted here}}
+  }
+  return result;
+};
+
+// Test a variety of valid constant evaluation paths
+struct S4 {
+  int i = 1;
+  constexpr S4() __attribute__((noinline)) {}
+};
+
+void* operator new(std::type_identity<S4>, size_t sz, std::align_val_t);
+void operator delete(std::type_identity<S4>, void *, size_t sz, std::align_val_t);
+
+constexpr int do_dynamic_alloc(int n) {
+  S4* s = new S4;
+  int result = n * s->i;
+  delete s;
+  return result;
+}
+
+template <int N> struct Tag {
+};
+
+static constexpr int force_do_dynamic_alloc = do_dynamic_alloc(5);
+
+constexpr int test_consteval_calling_constexpr(int i) {
+  if consteval {
+    return do_dynamic_alloc(2 * i);
+  }
+  return do_dynamic_alloc(3 * i);
+}
+
+int test_consteval(int n, Tag<test_consteval_calling_constexpr(2)>, Tag<do_dynamic_alloc(3)>) {
+  static const int t1 = test_consteval_calling_constexpr(4);
+  static const int t2 = do_dynamic_alloc(5);
+  int t3 = test_consteval_calling_constexpr(6);
+  int t4 = do_dynamic_alloc(7);
+  return t1 * t2 * t3 * t4;
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp b/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
new file mode 100644
index 0000000000000..b1c73236476c4
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions    -fsized-deallocation    -faligned-allocation  -Wall -Wpedantic
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation    -faligned-allocation  -Wall -Wpedantic
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation  -Wall -Wpedantic
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions    -fsized-deallocation -fno-aligned-allocation  -Wall -Wpedantic
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+
+struct BasicTypeAwareArrayAllocator {
+   template <typename T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t) = delete; // #1
+   template <typename T> void  operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #2
+};
+void *operator new[](std::type_identity<BasicTypeAwareArrayAllocator>, size_t, std::align_val_t);
+void  operator delete[](std::type_identity<BasicTypeAwareArrayAllocator>, void*, size_t, std::align_val_t);
+
+struct BasicTypeAwareNonArrayAllocator {
+   template <typename T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t);
+   template <typename T> void  operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
+   void *operator new(size_t) = delete;
+   void operator delete(void*) = delete;
+};
+
+struct WorkingTypeAwareAllocator {
+   template <typename T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t);
+   template <typename T> void  operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
+};
+
+void *operator new[](std::type_identity<WorkingTypeAwareAllocator>, size_t, std::align_val_t) = delete;
+void  operator delete[](std::type_identity<WorkingTypeAwareAllocator>, void*, size_t, std::align_val_t) = delete;
+
+
+void test() {
+  BasicTypeAwareArrayAllocator *A0 = new BasicTypeAwareArrayAllocator[10];
+  // expected-error at -1 {{call to deleted function 'operator new[]'}}
+  // expected-note@#1 {{candidate function [with T = BasicTypeAwareArrayAllocator] has been explicitly deleted}}
+  delete [] A0;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#2 {{'operator delete[]<BasicTypeAwareArrayAllocator>' has been explicitly marked deleted here}}
+  
+  BasicTypeAwareNonArrayAllocator *A1 = new BasicTypeAwareNonArrayAllocator[10];
+  delete [] A1;
+
+  WorkingTypeAwareAllocator *A2 = new WorkingTypeAwareAllocator[10];
+  // expected-note at -1 {{allocated with 'new[]' here}}
+  delete A2;
+  // expected-warning at -1 {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+
+  WorkingTypeAwareAllocator *A3 = new WorkingTypeAwareAllocator;
+  // expected-note at -1 {{allocated with 'new' here}}
+  delete [] A3;
+  // expected-warning at -1 {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}
+
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp b/clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
new file mode 100644
index 0000000000000..c85b92718479a
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26    -fsized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -fno-sized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -fno-sized-deallocation -fno-aligned-allocation
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26    -fsized-deallocation -fno-aligned-allocation
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+
+struct TestType {};
+template <typename T> struct TemplateTestType {};
+
+// Valid free declarations
+void *operator new(std::type_identity<int>, size_t, std::align_val_t); // #1
+void *operator new(std::type_identity<int>, size_t, std::align_val_t, TestType&); // #2
+template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #3
+template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, TestType&); // #4
+template <typename T> void *operator new(std::type_identity<TemplateTestType<T>>, size_t, std::align_val_t, TestType&); // #5
+template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, std::align_val_t, TemplateTestType<U>&); // #6
+template <template <typename> class T> void *operator new(std::type_identity<T<int>>, size_t, std::align_val_t); // #7
+
+void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #8
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #9
+template <typename T> void operator delete(std::type_identity<TemplateTestType<T>>, void *, size_t, std::align_val_t); // #10
+template <template <typename> class T> void operator delete(std::type_identity<T<int>>, void *, size_t, std::align_val_t); // #11
+
+typedef std::type_identity<float> TypeIdentityAlias1;
+void *operator new(TypeIdentityAlias1, size_t, std::align_val_t); // #12
+
+using TypeIdentityAlias2 = std::type_identity<double>;
+void *operator new(TypeIdentityAlias2, size_t, std::align_val_t); // #13
+
+template <typename T> using TypeIdentityAlias3 = std::type_identity<T>;
+template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t, std::align_val_t); // #14
+
+template <typename T> void *operator new(T, size_t, std::align_val_t);
+// expected-error at -1 {{'operator new' cannot take a dependent type as its 1st parameter}}
+
+template <typename T> void operator delete(T, void*, size_t, std::align_val_t);
+// expected-error at -1 {{'operator delete' cannot take a dependent type as its 1st parameter}}
+
+template <typename T> struct S {
+  typedef std::type_identity<T> type_identity;
+  typedef size_t size_ty;
+  typedef std::align_val_t align_val_ty;
+  typedef void *ptr_ty;
+};
+
+template <typename T> void *operator new(typename S<T>::type_identity, size_t, std::align_val_t);
+// expected-error at -1 {{'operator new' cannot take a dependent type as its 1st parameter}}
+
+// Invalid type aware declarations
+void *operator new(std::type_identity<int>, size_t); 
+// expected-error at -1 {{type aware 'operator new' must have at least three parameters}}
+void *operator new(std::type_identity<int>, size_t, TestType&);
+// expected-error at -1 {{type aware 'operator new' takes type std::align_val_t ('std::align_val_t') as 3rd parameter}}
+void operator delete(std::type_identity<int>, void *);
+// expected-error at -1 {{type aware 'operator delete' must have at least four parameters}}
+void operator delete(std::type_identity<int>, void *, size_t);
+// expected-error at -1 {{type aware 'operator delete' must have at least four parameters}}
+void operator delete(std::type_identity<int>, void *, std::align_val_t);
+// expected-error at -1 {{type aware 'operator delete' must have at least four parameters}}
+template <typename T> void operator delete(std::type_identity<T>, void *);
+// expected-error at -1 {{type aware 'operator delete' must have at least four parameters}}
+template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t);
+// expected-error at -1 {{type aware 'operator delete' must have at least four parameters}}
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t);
+// expected-error at -1 {{type aware 'operator delete' must have at least four parameters}}
+template <typename T, typename U> void *operator new(std::type_identity<T>, U);
+// expected-error at -1 {{type aware 'operator new' must have at least three parameters}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, U, size_t, std::align_val_t);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter; use 'void *' instead}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, void *, U, std::align_val_t);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as its 3rd parameter; use 'unsigned long' instead}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, void *, size_t, U);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as its 4th parameter; use 'std::align_val_t' instead}}
+template <typename U> void *operator new(std::type_identity<int>, typename S<U>::size_ty, std::align_val_t);
+// expected-error at -1 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter; use size_t ('unsigned long') instead}}
+template <typename U> void operator delete(std::type_identity<int>, typename S<U>::ptr_ty, size_t, std::align_val_t);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter; use 'void *' instead}}
+template <typename T, typename U> void *operator new(std::type_identity<T>, typename S<U>::size_ty, std::align_val_t);
+// expected-error at -1 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter; use size_t ('unsigned long') instead}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, typename S<U>::ptr_ty, size_t, std::align_val_t);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter; use 'void *' instead}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, void *, size_t, typename S<U>::align_val_ty);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as its 4th parameter; use 'std::align_val_t' instead}}
+
+template <typename T> using Alias = T;
+template <typename T> using TypeIdentityAlias = std::type_identity<T>;
+typedef std::type_identity<double> TypedefAlias;
+using UsingAlias = std::type_identity<float>;
+void *operator new(Alias<size_t>, std::align_val_t);
+template <typename T> void *operator new(Alias<std::type_identity<T>>, Alias<size_t>, std::align_val_t);
+void *operator new(Alias<std::type_identity<int>>, size_t, std::align_val_t);
+template <typename T> void operator delete(Alias<std::type_identity<T>>, void *, size_t, std::align_val_t);
+void operator delete(Alias<std::type_identity<int>>, void *, size_t, std::align_val_t);
+
+template <typename T> void *operator new(TypeIdentityAlias<T>, size_t, std::align_val_t);
+void *operator new(TypeIdentityAlias<int>, size_t, std::align_val_t);
+template <typename T> void operator delete(TypeIdentityAlias<T>, void *, size_t, std::align_val_t);
+void operator delete(TypeIdentityAlias<int>, void *, size_t, std::align_val_t);
+
+template <typename T> void *operator new(TypedefAlias, size_t, std::align_val_t);
+void *operator new(TypedefAlias, size_t, std::align_val_t);
+template <typename T> void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
+void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
+
+template <typename T> void *operator new(UsingAlias, size_t, std::align_val_t);
+void *operator new(UsingAlias, size_t, std::align_val_t);
+template <typename T> void operator delete(UsingAlias, void *, size_t, std::align_val_t);
+void operator delete(UsingAlias, void *, size_t, std::align_val_t);
+
+class ForwardDecl;
+void *operator new(std::type_identity<ForwardDecl>, size_t, std::align_val_t);
+void operator delete(std::type_identity<ForwardDecl>, void*, size_t, std::align_val_t);

diff  --git a/clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp b/clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp
new file mode 100644
index 0000000000000..34bd1d4206be1
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp
@@ -0,0 +1,150 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify=expected,precxx26 %s           -std=c++23
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s                             -std=c++26
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+
+// Basic valid declarations
+struct S {
+  void *operator new(std::type_identity<S>, size_t, std::align_val_t); // #1
+  void operator delete(std::type_identity<S>, void *, size_t, std::align_val_t); // #2
+  // precxx26-warning@#1 {{type aware allocators are a C++2c extension}}
+  // precxx26-warning@#2 {{type aware allocators are a C++2c extension}}
+  void operator delete(S *, std::destroying_delete_t);
+};
+
+template <typename T> struct S2 {
+  void *operator new(std::type_identity<S2<T>>, size_t, std::align_val_t); // #3
+  void operator delete(std::type_identity<S2<T>>, void *, size_t, std::align_val_t); // #4
+  // precxx26-warning@#3 {{type aware allocators are a C++2c extension}}
+  // precxx26-warning@#4 {{type aware allocators are a C++2c extension}}
+  void operator delete(S2 *, std::destroying_delete_t);
+};
+
+struct S3 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #5
+  template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #6
+  // precxx26-warning@#5 {{type aware allocators are a C++2c extension}}
+  // precxx26-warning@#6 {{type aware allocators are a C++2c extension}}
+  void operator delete(S3 *, std::destroying_delete_t);
+};
+
+struct S4 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #7
+  template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #8
+  template <typename T> void operator delete(std::type_identity<T>, S4 *, std::destroying_delete_t, size_t, std::align_val_t); // #9
+  // precxx26-warning@#7 {{type aware allocators are a C++2c extension}}
+  // precxx26-warning@#8 {{type aware allocators are a C++2c extension}}
+  // expected-error@#9 {{destroying delete is not permitted to be type aware}}
+};
+
+struct S5 {
+  template <typename T> void operator delete(std::type_identity<T>, T *, size_t, std::align_val_t); // #10
+  // expected-error@#10 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter}}
+  // precxx26-warning@#10 {{type aware allocators are a C++2c extension}}
+};
+
+struct S6 {
+  template <typename T> void *operator new(std::type_identity<S6>, T, std::align_val_t); // #11
+  // expected-error@#11 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter}}
+  // precxx26-warning@#11 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(std::type_identity<S6>, T, size_t, std::align_val_t); // #12
+  // expected-error@#12 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter}}
+  // precxx26-warning@#12 {{type aware allocators are a C++2c extension}}
+};
+
+template <typename U>
+struct S7 {
+  template <typename T> void *operator new(std::type_identity<T>, U, std::align_val_t); // #13
+  // expected-error@#13 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter;}}
+  // precxx26-warning@#13 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(std::type_identity<T>, U, size_t, std::align_val_t); // #14
+  // expected-error@#14 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter;}}
+  // precxx26-warning@#14 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(std::type_identity<T>, S7 *, std::destroying_delete_t, U, std::align_val_t); // #15
+  // expected-error@#15 {{destroying delete is not permitted to be type aware}}
+  void operator delete(S7 *, std::destroying_delete_t, U); // #16
+};
+
+void f() {
+  S7<int> s;
+  // expected-note at -1 {{in instantiation of template class 'S7<int>' requested here}}
+  // expected-error@#16 {{destroying operator delete can have only an optional size and optional alignment parameter}}
+}
+
+struct S8 {
+  template <typename T, typename U> void *operator new(std::type_identity<T>, U, std::align_val_t); // #17
+  // expected-error@#17 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter;}}
+  // precxx26-warning@#17 {{type aware allocators are a C++2c extension}}
+  template <typename T, typename U> void operator delete(std::type_identity<T>, U, size_t, std::align_val_t); // #18
+  // expected-error@#18 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter;}}
+  // precxx26-warning@#18 {{type aware allocators are a C++2c extension}}
+  template <typename T, typename U> void operator delete(std::type_identity<T>, S8 *, std::destroying_delete_t, U, std::align_val_t); // #19
+  // expected-error@#19 {{destroying delete is not permitted to be type aware}}
+};
+
+template <typename T> using Alias = T;
+template <typename T> using TypeIdentityAlias = std::type_identity<T>;
+typedef std::type_identity<double> TypedefAlias;
+using UsingAlias = std::type_identity<float>;
+struct S9 {
+  void *operator new(Alias<size_t>, std::align_val_t);
+  template <typename T> void *operator new(Alias<std::type_identity<T>>, Alias<size_t>, std::align_val_t); // #20
+  // precxx26-warning@#20 {{type aware allocators are a C++2c extension}}
+  void *operator new(Alias<std::type_identity<int>>, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(Alias<std::type_identity<T>>, void *, size_t, std::align_val_t); // #21
+  // precxx26-warning@#21{{type aware allocators are a C++2c extension}}
+  void operator delete(Alias<std::type_identity<int>>, void *, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+};
+struct S10 {
+  template <typename T> void *operator new(TypeIdentityAlias<T>, size_t, std::align_val_t); // #22
+  // precxx26-warning@#22 {{type aware allocators are a C++2c extension}}
+  void *operator new(TypeIdentityAlias<int>, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(TypeIdentityAlias<T>, void *, size_t, std::align_val_t); // #23
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  void operator delete(TypeIdentityAlias<int>, void *, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+};
+
+void test() {
+  S9 *s9 = new S9;
+  delete s9;
+  S10 *s10 = new S10;
+  delete s10;
+}
+
+struct S11 {
+  template <typename T> void *operator new(TypedefAlias, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  void *operator new(TypedefAlias, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+};
+struct S12 {
+  template <typename T> void *operator new(UsingAlias, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  void *operator new(UsingAlias, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  template <typename T> void operator delete(UsingAlias, void *, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  void operator delete(UsingAlias, void *, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+};
+
+struct S13 {
+  void *operator new(std::type_identity<S13>, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+  void operator delete(std::type_identity<S13>, void*, size_t, std::align_val_t);
+  // precxx26-warning at -1 {{type aware allocators are a C++2c extension}}
+};

diff  --git a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
new file mode 100644
index 0000000000000..786899295f627
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -0,0 +1,472 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s        -std=c++26 -fexceptions    -fsized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s        -std=c++26 -fexceptions -fno-sized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s        -std=c++26 -fexceptions    -fsized-deallocation -fno-aligned-allocation
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s        -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+#if defined(__cpp_aligned_new)
+#define ALLOCATION_ALIGNMENT , std::align_val_t
+#else
+#define ALLOCATION_ALIGNMENT
+#endif
+
+using size_t = __SIZE_TYPE__;
+
+void *operator new(size_t);
+void *operator new(size_t, std::align_val_t);
+void operator delete(void *);
+
+struct UntypedInclassNew {
+  void *operator new(size_t) = delete; // #1
+  void  operator delete(void *) = delete; //#2
+};
+void *operator new(std::type_identity<UntypedInclassNew>, size_t, std::align_val_t); // #3
+void  operator delete(std::type_identity<UntypedInclassNew>, void*, size_t, std::align_val_t); // #4
+
+
+struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_NoAlignedAlloc {
+  void *operator new(size_t) = delete; // #5
+  void  operator delete(void *) = delete; // #6
+};
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, size_t, std::align_val_t); // #7
+void operator delete(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, void *, size_t, std::align_val_t); // #8
+
+struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_AlignedAlloc {
+  void *operator new(size_t ALLOCATION_ALIGNMENT) = delete; // #9
+  void  operator delete(void * ALLOCATION_ALIGNMENT) = delete; // #10
+};
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, size_t, std::align_val_t); // #11
+void  operator delete(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, void *, size_t, std::align_val_t); // #12
+
+struct BasicClass {};
+void *operator new(std::type_identity<BasicClass>, size_t, std::align_val_t) = delete; // #13
+void  operator delete(std::type_identity<BasicClass>, void *, size_t, std::align_val_t) = delete; // #14
+
+struct InclassNew1 {
+  void *operator new(std::type_identity<InclassNew1>, size_t, std::align_val_t) = delete; // #15
+  void  operator delete(std::type_identity<InclassNew1>, void *, size_t, std::align_val_t) = delete; // #16
+};
+void *operator new(std::type_identity<InclassNew1>, size_t, std::align_val_t); // #17
+void  operator delete(std::type_identity<InclassNew1>, void *, size_t, std::align_val_t); // #18
+
+struct InclassNew2 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #19
+  template <typename T> void  operator delete(std::type_identity<T>, void *, size_t, std::align_val_t) = delete; // #20
+};
+void *operator new(std::type_identity<InclassNew2>, size_t, std::align_val_t); // #21
+void  operator delete(std::type_identity<InclassNew2>, void *, size_t, std::align_val_t); // #22
+
+struct InclassNew3 {
+  void *operator new(std::type_identity<InclassNew3>, size_t, std::align_val_t) = delete; // #23
+  void  operator delete(std::type_identity<InclassNew3>, void*, size_t, std::align_val_t) = delete; // #24
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #25
+  template <typename T> void  operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #26
+};
+
+struct __attribute__((aligned(128))) InclassNew4 {
+  void *operator new(std::type_identity<InclassNew4>, size_t, std::align_val_t); // #27
+  void  operator delete(std::type_identity<InclassNew4>, void*, size_t, std::align_val_t); // #28
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #29
+  template <typename T> void  operator delete(std::type_identity<T>, void *, size_t, std::align_val_t) = delete; // #30
+};
+
+struct InclassNew5 {
+  InclassNew5();
+  void *operator new(std::type_identity<InclassNew5>, size_t, std::align_val_t); // #31
+  void  operator delete(void *); // #32
+  void  operator delete(std::type_identity<InclassNew5>, void*, size_t, std::align_val_t) = delete; // #33
+};
+
+struct InclassNew6 {
+  // expected-error at -1 {{declaration of type aware 'operator delete' in 'InclassNew6' must have matching type aware 'operator new'}}
+  // expected-note@#36 {{unmatched type aware 'operator delete' declared here}}
+  InclassNew6();
+  void *operator new(size_t); // #34
+  void  operator delete(void *) = delete; // #35
+  void  operator delete(std::type_identity<InclassNew6>, void*, size_t, std::align_val_t) = delete; // #36
+};
+
+struct InclassNew7 {
+  InclassNew7();
+  void *operator new(std::type_identity<InclassNew7>, size_t, std::align_val_t); // #37
+  void  operator delete(std::type_identity<InclassNew7>, void*, size_t, std::align_val_t); // #38
+  void  operator delete(InclassNew7 *, std::destroying_delete_t) = delete; // #39
+};
+
+struct InclassNew8 {
+  // expected-error at -1 {{declaration of type aware 'operator new' in 'InclassNew8' must have matching type aware 'operator delete'}}
+  // expected-note@#40 {{unmatched type aware 'operator new' declared here}}
+  InclassNew8();
+  void *operator new(std::type_identity<InclassNew8>, size_t, std::align_val_t); // #40
+  void operator delete(void*); // #41
+};
+
+struct InclassNew9 {
+  // expected-error at -1 {{declaration of type aware 'operator new' in 'InclassNew9' must have matching type aware 'operator delete'}}
+  // expected-note@#42 {{unmatched type aware 'operator new' declared here}}
+  InclassNew9();
+  void *operator new(std::type_identity<InclassNew9>, size_t, std::align_val_t); // #42
+};
+
+void operator delete(std::type_identity<InclassNew9>, void*, size_t, std::align_val_t); // #43
+
+struct BaseClass1 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #44
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #45
+  virtual ~BaseClass1();
+};
+BaseClass1::~BaseClass1() {
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#45 {{'operator delete<BaseClass1>' has been explicitly marked deleted here}}
+}
+
+struct SubClass1 : BaseClass1 { 
+  virtual ~SubClass1();
+};
+
+SubClass1::~SubClass1() {
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#45 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
+}
+
+struct BaseClass2 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #46
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #47
+  void operator delete(BaseClass2 *, std::destroying_delete_t);  // #48
+  virtual ~BaseClass2();
+};
+BaseClass2::~BaseClass2(){
+};
+
+struct SubClass2 : BaseClass2 {
+  SubClass2(); // Force exception cleanup which should invoke type aware delete
+  virtual ~SubClass2();
+};
+SubClass2::~SubClass2(){
+}
+
+struct BaseClass3 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #49
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #50
+  void operator delete(BaseClass3 *, std::destroying_delete_t) = delete; // #51
+  virtual ~BaseClass3();
+};
+BaseClass3::~BaseClass3(){
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#51 {{'operator delete' has been explicitly marked deleted here}}
+}
+
+struct SubClass3 : BaseClass3 {
+  virtual ~SubClass3();
+};
+SubClass3::~SubClass3(){
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#51 {{'operator delete' has been explicitly marked deleted here}}
+}
+
+template <typename A, typename B> concept Derived = requires (A * a, B *b) { a = b; };
+template <typename A, typename B> concept Same = requires (std::type_identity<A> * a, std::type_identity<B> *b) { a = b; };
+
+struct SubClass4;
+struct BaseClass4 {
+  template <Derived<SubClass4> T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #52
+  template <Derived<SubClass4> T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #53
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #54
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #55
+
+  virtual ~BaseClass4();
+};
+BaseClass4::~BaseClass4() {
+}
+
+struct SubClass4 : BaseClass4 {
+  virtual ~SubClass4();
+};
+SubClass4::~SubClass4(){
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#53 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
+}
+
+struct SubClass4_1 : SubClass4 {
+  SubClass4_1();
+};
+struct SubClass4_2 : BaseClass4 {
+};
+
+struct SubClass5;
+struct BaseClass5 {
+  template <Same<SubClass5> T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #56
+  template <Same<SubClass5> T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #57
+  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #58
+  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #59
+};
+
+struct SubClass5 : BaseClass5 {
+};
+struct SubClass5_1 : SubClass5 {
+};
+
+
+struct BaseClass6 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #60
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #61
+  BaseClass6();
+  virtual ~BaseClass6();
+};
+BaseClass6::~BaseClass6(){
+}
+
+struct SubClass6_1 : BaseClass6 {
+  // expected-error at -1 {{declaration of type aware 'operator new' in 'SubClass6_1' must have matching type aware 'operator delete'}}
+  // expected-note@#62 {{unmatched type aware 'operator new' declared here}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #62
+  SubClass6_1();
+};
+struct SubClass6_2 : BaseClass6 {
+  // expected-error at -1 {{declaration of type aware 'operator delete' in 'SubClass6_2' must have matching type aware 'operator new'}}
+  // expected-note@#63 {{unmatched type aware 'operator delete' declared here}}
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #63
+  SubClass6_2();
+};
+
+struct MultiDimensionArrayTest1 {
+  int i;
+  MultiDimensionArrayTest1();
+  template <typename T, unsigned N> void *operator new[](std::type_identity<T[N]>, size_t, std::align_val_t) = delete; // #64
+  template <typename T, unsigned N> void operator delete[](std::type_identity<T[N]>, void*, size_t, std::align_val_t) = delete; // #65
+};
+
+struct MultiDimensionArrayTest2 {
+  int i;
+  MultiDimensionArrayTest2();
+  template <unsigned N> void *operator new[](std::type_identity<MultiDimensionArrayTest2[N]>, size_t, std::align_val_t) = delete; // #66
+  template <unsigned N> void operator delete[](std::type_identity<MultiDimensionArrayTest2[N]>, void*, size_t, std::align_val_t) = delete; // #67
+};
+
+struct MultiDimensionArrayTest3 {
+  int i;
+  MultiDimensionArrayTest3();
+  template <unsigned N> requires (N%4 == 0) void *operator new[](std::type_identity<MultiDimensionArrayTest3[N]>, size_t, std::align_val_t) = delete; // #68
+  template <unsigned N> requires (N%4 == 0) void operator delete[](std::type_identity<MultiDimensionArrayTest3[N]>, void*, size_t, std::align_val_t) = delete; // #69
+};
+
+struct ClassScopedTemplatePackStruct {
+  template <class T, class... Pack> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Pack...);
+  template <class T, class... Pack> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Pack...); // #70
+};
+
+void test() {
+  
+  // untyped in class declaration wins
+  UntypedInclassNew *O1 = new UntypedInclassNew;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#1 {{candidate function has been explicitly deleted}}
+  delete O1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#2 {{'operator delete' has been explicitly marked deleted here}}
+
+  // untyped in class declaration wins, even though global is aligned and in class is not
+  UntypedInclassNewOveraligned_NoAlignedAlloc *O2 = new UntypedInclassNewOveraligned_NoAlignedAlloc;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#5 {{candidate function has been explicitly deleted}}
+  delete O2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#6 {{'operator delete' has been explicitly marked deleted here}}
+
+  // untyped in class declaration wins
+  UntypedInclassNewOveraligned_AlignedAlloc *O3 = new UntypedInclassNewOveraligned_AlignedAlloc;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#9 {{candidate function has been explicitly deleted}}
+  delete O3;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#10 {{'operator delete' has been explicitly marked deleted here}}
+
+  // We resolve the explicitly typed free operator
+  BasicClass *O4 = new BasicClass;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#13 {{candidate function has been explicitly deleted}}
+  // expected-note@#3 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<UntypedInclassNew>' for 1st argument}}
+  // expected-note@#17 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<InclassNew1>' for 1st argument}}
+  // expected-note@#21 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<InclassNew2>' for 1st argument}}
+  // expected-note@#7 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>' for 1st argument}}
+  // expected-note@#11 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<UntypedInclassNewOveraligned_AlignedAlloc>' for 1st argument}}
+
+  delete O4;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#14 {{'operator delete' has been explicitly marked deleted here}}
+
+  // We resolve the explicitly typed in class operator
+  InclassNew1 *O5 = new InclassNew1;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#15 {{candidate function has been explicitly deleted}}
+  delete O5;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#16 {{'operator delete' has been explicitly marked deleted here}}
+
+  // We resolve the unconstrained in class operators over the constrained free operators
+  InclassNew2 *O6 = new InclassNew2;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#19 {{candidate function [with T = InclassNew2] has been explicitly deleted}}
+  delete O6;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#20 {{'operator delete<InclassNew2>' has been explicitly marked deleted here}}
+
+  // We prefer the constrained in class operators over the unconstrained variants
+  InclassNew3 *O7 = new InclassNew3;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#23 {{candidate function has been explicitly deleted}}
+  // expected-note@#25 {{candidate function [with T = InclassNew3]}}
+  delete O7;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#24 {{'operator delete' has been explicitly marked deleted here}}
+
+  // Constructor clean up invokes typed operator if typed new was used
+  InclassNew5 *O9 = new InclassNew5;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#33 {{'operator delete' has been explicitly marked deleted here}}
+  delete O9;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#33 {{'operator delete' has been explicitly marked deleted here}}
+
+  // Constructor clean up invokes untyped delete if untyped delete was used
+  InclassNew6 *O10 = new InclassNew6;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#36 {{'operator delete' has been explicitly marked deleted here}}
+  // expected-error at -3 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
+  // expected-note@#34 {{non-type aware 'operator new' declared here in 'InclassNew6'}}
+  // expected-note@#36 {{type aware 'operator delete' declared here}}
+  delete O10;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#36 {{'operator delete' has been explicitly marked deleted here}}
+
+  // Destroying delete is prefered over typed delete
+  InclassNew7 *O11 = new InclassNew7;
+  delete O11;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#39 {{'operator delete' has been explicitly marked deleted here}}
+
+  InclassNew8 *O12 = new InclassNew8;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#40 {{type aware 'operator new' declared here in 'InclassNew8'}}
+  // expected-note@#41 {{non-type aware 'operator delete' declared here}}
+  delete O12;
+
+  InclassNew9 *O13 = new InclassNew9;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#42 {{type aware 'operator new' declared here in 'InclassNew9'}}
+
+  delete O13;
+
+  // Creating the virtual destructor for an type requires the deleting destructor
+  // for that type
+  SubClass1 *O14 = new SubClass1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#45 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
+
+  delete O14;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#45 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
+
+  SubClass2 *O15 = new SubClass2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#47 {{'operator delete<SubClass2>' has been explicitly marked deleted here}}
+  delete O15;
+
+  // Deletion triggers destroying delete despite type aware delete
+  SubClass3 *O16 = new SubClass3;
+  delete O16;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#51 {{'operator delete' has been explicitly marked deleted here}}
+
+  SubClass4 *O17 = new SubClass4;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#52 {{candidate function [with T = SubClass4] has been explicitly deleted}}
+  // expected-note@#54 {{candidate function [with T = SubClass4]}}
+  delete O17;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#53 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
+
+  SubClass4_1 *O18 = new SubClass4_1;
+  delete O18;
+
+  SubClass4_2 *O19 = new SubClass4_2;
+  delete O19;
+
+  SubClass5 *O20 = new SubClass5;
+  delete O20;
+
+  SubClass5_1 *O21 = new SubClass5_1;
+  // expected-error at -1 {{no matching function for call to 'operator new'}}
+  delete O21;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'SubClass5_1'}}
+  // expected-note@#57 {{member 'operator delete' declared here}}
+  // expected-note@#59 {{member 'operator delete' declared here}}
+
+  SubClass6_1 *O22 = new SubClass6_1;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#62 {{type aware 'operator new' declared here in 'SubClass6_1'}}
+  // expected-note@#61 {{type aware 'operator delete' declared here in 'BaseClass6'}}
+  delete O22;
+
+  SubClass6_2 *O23 = new SubClass6_2;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+  // expected-note@#60 {{type aware 'operator new' declared here in 'BaseClass6'}}
+  // expected-note@#63 {{type aware 'operator delete' declared here in 'SubClass6_2'}}
+  delete O23;
+
+  MultiDimensionArrayTest1 *O24 = new MultiDimensionArrayTest1;
+  delete O24;
+
+  MultiDimensionArrayTest1 *O25 = new MultiDimensionArrayTest1[10];
+  // expected-error at -1 {{no matching function for call to 'operator new[]'}}
+  delete [] O25;
+  // expected-error at -1 {{no suitable member 'operator delete[]' in 'MultiDimensionArrayTest1'}}
+  // expected-note@#65 {{member 'operator delete[]' declared here}}
+
+  {
+    using InnerArray = MultiDimensionArrayTest1[3];
+    InnerArray *O26 = new InnerArray[7];
+    // expected-error at -1 {{call to deleted function 'operator new[]'}}
+    // expected-note@#64 {{candidate function [with T = MultiDimensionArrayTest1, N = 3] has been explicitly deleted}}
+    delete [] O26;
+    // expected-error at -1 {{attempt to use a deleted function}}
+    // expected-note@#65 {{'operator delete[]<MultiDimensionArrayTest1, 3U>' has been explicitly marked deleted here}}
+  }
+  {
+    using InnerArray = MultiDimensionArrayTest2[3];
+    InnerArray *O27 = new InnerArray[7];
+    // expected-error at -1 {{call to deleted function 'operator new[]'}}
+    // expected-note@#66 {{candidate function [with N = 3] has been explicitly deleted}}
+    delete [] O27;
+    // expected-error at -1 {{attempt to use a deleted function}}
+    // expected-note@#67 {{'operator delete[]<3U>' has been explicitly marked deleted here}}
+  }
+  {
+    using InnerArray = MultiDimensionArrayTest3[3];
+    InnerArray *O28 = new InnerArray[3];
+    // expected-error at -1 {{no matching function for call to 'operator new[]'}}
+    delete [] O28;
+    // expected-error at -1 {{no suitable member 'operator delete[]' in 'MultiDimensionArrayTest3'}}
+    // expected-note@#69 {{member 'operator delete[]' declared here}}
+  }
+  {
+    using InnerArray = MultiDimensionArrayTest3[4];
+    InnerArray *O29 = new InnerArray[3];
+    // expected-error at -1 {{call to deleted function 'operator new[]'}}
+    // expected-note@#68 {{candidate function [with N = 4] has been explicitly deleted}}
+    delete [] O29;
+    // expected-error at -1 {{attempt to use a deleted function}}
+    // expected-note@#69 {{'operator delete[]<4U>' has been explicitly marked deleted here}}
+  }
+  {
+    ClassScopedTemplatePackStruct *O30 = new ClassScopedTemplatePackStruct;
+    delete O30;
+    // expected-error at -1 {{no suitable member 'operator delete' in 'ClassScopedTemplatePackStruct'}}
+    // expected-note@#70 {{member 'operator delete' declared here}}
+  }
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp b/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
new file mode 100644
index 0000000000000..af5386b25451f
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
@@ -0,0 +1,114 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26    -fsized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26 -fno-sized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26 -fno-sized-deallocation -fno-aligned-allocation
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26    -fsized-deallocation -fno-aligned-allocation
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+
+
+template <class Tp> struct is_const {
+  static const bool value = false;
+};
+template <class Tp> struct is_const<Tp const> {
+  static const bool value = true;
+};
+
+template <class Tp> struct is_volatile {
+  static const bool value = false;
+};
+template <class Tp> struct is_volatile<Tp volatile> {
+  static const bool value = true;
+};
+
+template <class T> static const bool is_const_v = is_const<T>::value;
+template <class T> static const bool is_volatile_v = is_volatile<T>::value;
+
+struct VerifyQualifiers {
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) throw() {
+    static_assert(is_const_v<T> == false); // #1
+    static_assert(is_volatile_v<T> == false); // #2
+    return 0;
+  }
+  template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) {
+    static_assert(is_const_v<T> == false); // #3
+    static_assert(is_volatile_v<T> == false); // #4
+  }
+  template <typename T> void *operator new(std::type_identity<_Atomic T>, size_t, std::align_val_t) throw() {
+    static_assert(is_const_v<T> == false);
+    static_assert(is_volatile_v<T> == false);
+  }
+};
+
+void *operator new(std::type_identity<VerifyQualifiers> type, size_t, std::align_val_t) throw() { // #11
+  static_assert(is_const_v<typename decltype(type)::type> == false); // #5
+  static_assert(is_volatile_v<typename decltype(type)::type> == false); // #6
+  return 0;
+}
+
+void operator delete(std::type_identity<VerifyQualifiers> type, void*, size_t, std::align_val_t) {
+  static_assert(is_const_v<typename decltype(type)::type> == false); // #7
+  static_assert(is_volatile_v<typename decltype(type)::type> == false); // #8
+}
+
+void *operator new(std::type_identity<int>, size_t, std::align_val_t) throw() = delete; // #12
+void operator delete(std::type_identity<int>, void*, size_t, std::align_val_t) = delete;
+
+struct TestAtomic1 {
+
+};
+struct TestAtomic2 {
+};
+
+void *operator new(std::type_identity<TestAtomic1>, size_t, std::align_val_t) throw() = delete; // #13
+void operator delete(std::type_identity<_Atomic TestAtomic1>, void*, size_t, std::align_val_t) = delete; // #9
+void *operator new(std::type_identity<_Atomic TestAtomic2>, size_t, std::align_val_t) = delete; // #10
+void operator delete(std::type_identity<TestAtomic2>, void*, size_t, std::align_val_t) = delete;
+
+// Success tests
+void test_member_allocators() {
+  auto *unqualified_obj = new VerifyQualifiers();
+  delete unqualified_obj;
+  auto *const_obj = new const VerifyQualifiers();
+  delete const_obj;
+  auto *volatile_obj = new volatile VerifyQualifiers();
+  delete volatile_obj;
+  auto *const_volatile_obj = new const volatile VerifyQualifiers();
+  delete const_volatile_obj;
+  auto *atomic_obj = new _Atomic VerifyQualifiers();
+  delete atomic_obj;
+  auto *atomic_test1 = new _Atomic TestAtomic1;
+  delete atomic_test1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#9 {{'operator delete' has been explicitly marked deleted here}}
+  auto *atomic_test2 = new _Atomic TestAtomic2;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#10 {{candidate function has been explicitly deleted}}
+  // expected-note@#11 {{candidate function not viable}}
+  // expected-note@#12 {{candidate function not viable}}
+  // expected-note@#13 {{candidate function not viable}}
+  delete atomic_test2;
+}
+
+
+
+void test_global_allocators() {
+  auto *unqualified_obj = ::new VerifyQualifiers();
+  ::delete unqualified_obj;
+  auto *const_obj = ::new const VerifyQualifiers();
+  ::delete const_obj;
+  auto *volatile_obj = ::new volatile VerifyQualifiers();
+  ::delete volatile_obj;
+  auto *const_volatile_obj = ::new const volatile VerifyQualifiers();
+  ::delete const_volatile_obj;
+  _Atomic VerifyQualifiers *atomic_obj = ::new _Atomic VerifyQualifiers();
+  ::delete atomic_obj;
+  _Atomic int *atomic_int = new _Atomic int;
+  delete atomic_int;
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp b/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
new file mode 100644
index 0000000000000..d0242e43edcf1
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=0
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=1
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=2
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=3
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=4
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s          -std=c++26 
+
+namespace std {
+#if !defined(INVALID_TYPE_IDENTITY_VERSION)
+  // expected-no-diagnostics
+  template <class T> struct type_identity {
+  };
+  #define TYPE_IDENTITY(T) std::type_identity<T>
+#elif INVALID_TYPE_IDENTITY_VERSION==0
+  struct type_identity {};
+  // expected-error at -1 {{std::type_identity must be a class template with a single type parameter}}
+  #define TYPE_IDENTITY(T) std::type_identity
+#elif INVALID_TYPE_IDENTITY_VERSION==1
+  template <class A, class B> struct type_identity {};
+  // expected-error at -1 {{std::type_identity must be a class template with a single type parameter}}
+  #define TYPE_IDENTITY(T) std::type_identity<T, int>
+#elif INVALID_TYPE_IDENTITY_VERSION==2
+  enum type_identity {};
+  // expected-error at -1 {{std::type_identity must be a class template with a single type parameter}}
+  #define TYPE_IDENTITY(T) std::type_identity
+#elif INVALID_TYPE_IDENTITY_VERSION==3
+  template <class T> using type_identity = int;
+  #define TYPE_IDENTITY(T) std::type_identity<T>
+#elif INVALID_TYPE_IDENTITY_VERSION==4
+  template <class T> struct inner {};
+  template <class T> using type_identity = inner<T>;
+  #define TYPE_IDENTITY(T) std::type_identity<T>
+#endif
+  using size_t = __SIZE_TYPE__;
+  enum class align_val_t : long {};
+}
+
+template <class T> void *operator new(TYPE_IDENTITY(T), std::size_t, std::align_val_t); // #operator_new
+template <class T> void operator delete(TYPE_IDENTITY(T), void*, std::size_t, std::align_val_t); // #operator_delete
+
+// These error messages aren't great, but they fall out of the way we model
+// alias types. Getting them in this way requires extremely unlikely code to be
+// used, so this is not terrible.
+
+#if INVALID_TYPE_IDENTITY_VERSION==3
+// expected-error@#operator_new {{'operator new' takes type size_t ('unsigned long') as 1st parameter}}
+// expected-error@#operator_delete {{1st parameter of 'operator delete' must have type 'void *'}}
+#elif INVALID_TYPE_IDENTITY_VERSION==4
+// expected-error@#operator_new {{'operator new' cannot take a dependent type as its 1st parameter; use size_t ('unsigned long') instead}}
+// expected-error@#operator_delete {{'operator delete' cannot take a dependent type as its 1st parameter; use 'void *' instead}}
+#endif
+
+using size_t = __SIZE_TYPE__;
+struct TestType {};
+
+void f() {
+  TestType *t = new TestType;
+  delete t;
+}

diff  --git a/clang/test/SemaCXX/type-aware-placement-operators.cpp b/clang/test/SemaCXX/type-aware-placement-operators.cpp
new file mode 100644
index 0000000000000..b64832a76e20b
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-placement-operators.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++26 -fexceptions -fcxx-exceptions    -fsized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation    -faligned-allocation
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation -fno-aligned-allocation
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++26 -fexceptions -fcxx-exceptions    -fsized-deallocation -fno-aligned-allocation
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+struct Context;
+struct S1 {
+  S1();
+};
+void *operator new(std::type_identity<S1>, size_t, std::align_val_t, Context&);
+void operator delete(std::type_identity<S1>, void*, size_t, std::align_val_t, Context&) = delete; // #1
+
+struct S2 {
+  S2();
+  template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&);
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Context&) = delete; // #2
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #3
+};
+
+struct S3 {
+  S3();
+  template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #4
+};
+
+struct S4 {
+  S4();
+  template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&); // #S4_new
+  template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #5
+};
+
+void test(Context& Ctx) {
+  S1 *s1 = new (Ctx) S1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#1 {{'operator delete' has been explicitly marked deleted here}}
+  delete s1;
+  S2 *s2_1 = new (Ctx) S2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#2 {{'operator delete<S2>' has been explicitly marked deleted here}}
+  // expected-note@#3 {{'operator delete<S2>' has been explicitly marked deleted here}}
+  delete s2_1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#2 {{'operator delete<S2>' has been explicitly marked deleted here}}
+  S2 *s2_2 = new (std::align_val_t(128), Ctx) S2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  delete s2_2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#3 {{'operator delete<S2>' has been explicitly marked deleted here}}
+  S3 *s3_1 = new S3;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
+  delete s3_1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
+  S3 *s3_2 = new (std::align_val_t(128)) S3;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
+  delete s3_2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
+
+  S4 *s4_1 = new (Ctx) S4;
+  // expected-error at -1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
+  // expected-note@#S4_new {{type aware 'operator new' declared here in 'S4'}}
+  delete s4_1;
+}


        


More information about the cfe-commits mailing list