[clang] [RFC] Initial implementation of P2719 (PR #113510)

Oliver Hunt via cfe-commits cfe-commits at lists.llvm.org
Sat Nov 9 01:44:18 PST 2024


https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/113510

>From 9730efc08b52431ee650ea0abf88b998e5057c8d Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 19 Oct 2024 18:46:23 -0700
Subject: [PATCH 1/6] Initial implementation of P2719

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);
  void  operator delete(std::type_identity<int>, void*);

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

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

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.

In order to ensure interchangability with existing operators, this implementation
does extend the definition of a `usual deallocation function` as suggested in the
proposal to support templated operators, as long as the only dependent parameter
is the type_identity tag type, and the tag is explicitly a specialization of
std::type_identity

The implementation adds a new `-fexperimental-cxx-type-aware-allocators` flag
to enable the feature. This is detectable in source via the `cxx_type_aware_allocators`
feature check.

This also adds a separate flag to support the application of the proposal
to the destroying delete, `-fexperimental-cxx-type-aware-destroying-delete`.
The proposal currently does not support destroying delete as it adds some
significant hazards, but we've received sufficient requests for the
behavior that we included it so people can see if it is actually useful.
This can be detected in source via the `cxx_type_aware_destroying_delete`
feature flag.

The implementation includes some error checking and warnings to try to
detect basic errors, but the problem of detecting mismatch type-polymorphic
new and delete is a problem to be explored in future.
---
 clang/include/clang/AST/Decl.h                |   2 +
 clang/include/clang/AST/ExprCXX.h             |  35 +-
 clang/include/clang/AST/Stmt.h                |   4 +
 clang/include/clang/AST/Type.h                |   5 +
 clang/include/clang/Basic/DiagnosticGroups.td |   4 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  12 +
 clang/include/clang/Basic/Features.def        |   4 +
 clang/include/clang/Basic/LangOptions.def     |   2 +
 clang/include/clang/Driver/Options.td         |   9 +-
 clang/include/clang/Sema/Sema.h               |  43 +-
 .../Core/PathSensitive/CallEvent.h            |   2 +-
 clang/lib/AST/ASTImporter.cpp                 |   8 +-
 clang/lib/AST/Decl.cpp                        |  37 +-
 clang/lib/AST/DeclCXX.cpp                     |  33 +-
 clang/lib/AST/ExprCXX.cpp                     |  17 +-
 clang/lib/AST/Type.cpp                        |  23 +
 clang/lib/CodeGen/CGExprCXX.cpp               |  70 ++-
 clang/lib/Driver/ToolChains/Clang.cpp         |   8 +
 clang/lib/Sema/SemaCoroutine.cpp              |  87 ++-
 clang/lib/Sema/SemaDecl.cpp                   |  13 +
 clang/lib/Sema/SemaDeclCXX.cpp                | 269 +++++++--
 clang/lib/Sema/SemaExprCXX.cpp                | 530 +++++++++++++-----
 clang/lib/Sema/SemaTemplate.cpp               |  12 +
 clang/lib/Serialization/ASTReaderStmt.cpp     |   1 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |   1 +
 clang/test/CodeGenCXX/new.cpp                 |   1 +
 clang/test/CodeGenCoroutines/coro-alloc-2.cpp |   2 +
 clang/test/CodeGenCoroutines/coro-alloc.cpp   |   3 +
 clang/test/Modules/new-delete.cpp             |   1 +
 clang/test/SemaCXX/coroutine-alloc-4.cpp      |  29 +-
 clang/test/SemaCXX/coroutine-allocs.cpp       |  12 +
 clang/test/SemaCXX/delete.cpp                 |   5 +
 .../test/SemaCXX/type-aware-new-constexpr.cpp |  73 +++
 .../SemaCXX/type-aware-new-delete-arrays.cpp  |  49 ++
 ...are-new-delete-basic-free-declarations.cpp |  95 ++++
 ...new-delete-basic-in-class-declarations.cpp |  64 +++
 ...type-aware-new-delete-basic-resolution.cpp | 302 ++++++++++
 37 files changed, 1580 insertions(+), 287 deletions(-)
 create mode 100644 clang/test/SemaCXX/type-aware-new-constexpr.cpp
 create mode 100644 clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
 create mode 100644 clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
 create mode 100644 clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp
 create mode 100644 clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 7ff35d73df5997..5235ace380bd9e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2530,6 +2530,8 @@ class FunctionDecl : public DeclaratorDecl,
   /// Determine whether this is a destroying operator delete.
   bool isDestroyingOperatorDelete() const;
 
+  bool IsTypeAwareOperatorNewOrDelete() const;
+
   /// Compute the language linkage.
   LanguageLinkage getLanguageLinkage() const;
 
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 57ab94bcb2010f..3433966a556594 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2234,6 +2234,17 @@ enum class CXXNewInitializationStyle {
   Braces
 };
 
+struct ImplicitAllocationParameters {
+  bool PassTypeIdentity;
+  bool PassAlignment;
+};
+
+struct ImplicitDeallocationParameters {
+  bool PassTypeIdentity;
+  bool PassAlignment;
+  bool PassSize;
+};
+
 /// Represents a new-expression for memory allocation and constructor
 /// calls, e.g: "new CXXNewExpr(foo)".
 class CXXNewExpr final
@@ -2289,7 +2300,7 @@ class CXXNewExpr final
 
   /// Build a c++ new expression.
   CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
-             FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
+             FunctionDecl *OperatorDelete, ImplicitAllocationParameters IAP,
              bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
              SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
              CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2304,7 +2315,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, ImplicitAllocationParameters IAP,
          bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
          SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
          CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2393,6 +2404,13 @@ class CXXNewExpr final
     return const_cast<CXXNewExpr *>(this)->getPlacementArg(I);
   }
 
+  unsigned getNumImplicitArgs() const {
+    unsigned ImplicitArgCount = 1; // Size
+    ImplicitArgCount += passAlignment();
+    ImplicitArgCount += passTypeIdentity();
+    return ImplicitArgCount;
+  }
+
   bool isParenTypeId() const { return CXXNewExprBits.IsParenTypeId; }
   SourceRange getTypeIdParens() const {
     return isParenTypeId() ? getTrailingObjects<SourceRange>()[0]
@@ -2431,6 +2449,12 @@ class CXXNewExpr final
   /// the allocation function.
   bool passAlignment() const { return CXXNewExprBits.ShouldPassAlignment; }
 
+  /// Indicates whether a type_identity tag should be implicitly passed to
+  /// the allocation function.
+  bool passTypeIdentity() const {
+    return CXXNewExprBits.ShouldPassTypeIdentity;
+  }
+
   /// Answers whether the usual array deallocation function for the
   /// allocated type expects the size of the allocation as a
   /// parameter.
@@ -2438,6 +2462,13 @@ class CXXNewExpr final
     return CXXNewExprBits.UsualArrayDeleteWantsSize;
   }
 
+  /// Provides the full set of information about expected implicit
+  /// parameters in this call
+  ImplicitAllocationParameters implicitAllocationParameters() const {
+    return ImplicitAllocationParameters{.PassTypeIdentity = passTypeIdentity(),
+                                        .PassAlignment = passAlignment()};
+  }
+
   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 83fafbabb1d460..8717b2b3e84846 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -890,6 +890,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/AST/Type.h b/clang/include/clang/AST/Type.h
index 1bcc7ee0b70dee..b7ee0f2f5cf046 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2636,6 +2636,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
                                                 // C++14 decltype(auto)
   bool isTypedefNameType() const;               // typedef or alias template
 
+  bool isTypeIdentitySpecialization() const; // std::type_identity<X> for any X
+  bool isDestroyingDeleteT() const;          // std::destroying_delete_t
+
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
   bool is##Id##Type() const;
 #include "clang/Basic/OpenCLImageTypes.def"
@@ -2700,6 +2703,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     return static_cast<TypeDependence>(TypeBits.Dependence);
   }
 
+  TemplateDecl *getSpecializedTemplateDecl() const;
+
   /// Whether this type is an error type.
   bool containsErrors() const {
     return getDependence() & TypeDependence::Error;
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 72eada50a56cc9..7fa46023593466 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1524,6 +1524,10 @@ in addition with the pragmas or -fmax-tokens flag to get any warnings.
 }];
 }
 
+// Warning group for type aware allocators
+def TypeAwareAllocatorMismatch :
+  DiagGroup<"type-aware-allocator-mismatch">;
+
 def WebAssemblyExceptionSpec : DiagGroup<"wasm-exception-spec">;
 
 def RTTI : DiagGroup<"rtti">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d697e6d61afa9a..1a21ed3f8da14e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9692,6 +9692,18 @@ def err_operator_delete_param_type : Error<
 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<
+  "type aware destroying delete is not permitted">;
+def err_unsupported_type_aware_allocator : Error<
+  "type aware allocation operators are disabled">;
+def err_no_matching_type_aware_cleanup_deallocator_mismatch : Error<
+  "type aware %0 requires matching %1 in %2">;
+def err_type_aware_operator_found : Note<
+  "type aware %0 found in %1">;
+def warn_mismatching_type_aware_cleanup_deallocator : Warning<
+  "mismatching type aware allocation operators for constructor cleanup">, InGroup<TypeAwareAllocatorMismatch>;
+def note_type_aware_operator_declared : Note<
+  "%select{|non-}0type aware %1 declared here">;
 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<
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 7f5d26118bdc71..a9ff58fb94ebc6 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -307,6 +307,10 @@ EXTENSION(datasizeof, LangOpts.CPlusPlus)
 
 FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables)
 
+// Type aware allocators
+FEATURE(cxx_type_aware_allocators, LangOpts.TypeAwareAllocators)
+FEATURE(cxx_type_aware_destroying_delete, LangOpts.TypeAwareDestroyingDelete)
+
 // CUDA/HIP Features
 FEATURE(cuda_noinline_keyword, LangOpts.CUDA)
 EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 39e4851dd3814c..ed9cb53727d612 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -312,6 +312,8 @@ LANGOPT(OpenACC           , 1, 0, "OpenACC Enabled")
 
 LANGOPT(MSVCEnableStdcMacro , 1, 0, "Define __STDC__ with '-fms-compatibility'")
 LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
+LANGOPT(TypeAwareAllocators , 1, 1, "type aware C++ allocation operators")
+LANGOPT(TypeAwareDestroyingDelete , 1, 1, "type aware C++ destroying delete")
 LANGOPT(AlignedAllocation , 1, 0, "aligned allocation")
 LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable")
 LANGOPT(NewAlignOverride  , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 805b79491e6ea4..3c7b41a38710cf 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3543,7 +3543,14 @@ def : Separate<["-"], "fnew-alignment">, Alias<fnew_alignment_EQ>;
 def : Flag<["-"], "faligned-new">, Alias<faligned_allocation>;
 def : Flag<["-"], "fno-aligned-new">, Alias<fno_aligned_allocation>;
 def faligned_new_EQ : Joined<["-"], "faligned-new=">;
-
+defm cxx_type_aware_allocators : BoolFOption<"experimental-cxx-type-aware-allocators",
+  LangOpts<"TypeAwareAllocators">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption], "Enable C++YY type aware allocator operators">,
+  NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;
+defm cxx_type_aware_destroying_delete : BoolFOption<"cxx-type-aware-destroying-delete",
+  LangOpts<"TypeAwareDestroyingDelete">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption], "Enable C++YY type aware allocator operators">,
+  NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;
 def fobjc_legacy_dispatch : Flag<["-"], "fobjc-legacy-dispatch">, Group<f_Group>;
 def fobjc_new_property : Flag<["-"], "fobjc-new-property">, Group<clang_ignored_f_Group>;
 defm objc_infer_related_result_type : BoolFOption<"objc-infer-related-result-type",
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 93d98e1cbb9c81..90bc45a5567faa 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2135,6 +2135,10 @@ class Sema final : public SemaBase {
            isConstantEvaluatedOverride;
   }
 
+  bool AllowTypeAwareAllocators() const {
+    return getLangOpts().TypeAwareAllocators && !isConstantEvaluatedContext();
+  }
+
   SourceLocation getLocationOfStringLiteralByte(const StringLiteral *SL,
                                                 unsigned ByteNo) const;
 
@@ -4751,6 +4755,15 @@ class Sema final : public SemaBase {
 
   CXXRecordDecl *getStdBadAlloc() const;
   EnumDecl *getStdAlignValT() const;
+  ClassTemplateDecl *getStdTypeIdentity() const;
+  std::optional<QualType> InstantiateSpecializedTypeIdentity(QualType Subject);
+  bool IsTypeIdentitySpecialization(QualType Type) const;
+  bool IsTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const;
+  bool IsTypeAwareOperatorNewOrDelete(const FunctionTemplateDecl *FnDecl) const;
+  bool IsTypeAwareOperatorNewOrDelete(const NamedDecl *FnDecl) const;
+  std::optional<FunctionDecl *>
+  InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnDecl,
+                                  QualType AllocType);
 
   ValueDecl *tryLookupUnambiguousFieldDecl(RecordDecl *ClassDecl,
                                            const IdentifierInfo *MemberOrBase);
@@ -7934,6 +7947,10 @@ class Sema final : public SemaBase {
   /// The C++ "type_info" declaration, which is defined in \<typeinfo>.
   RecordDecl *CXXTypeInfoDecl;
 
+  /// The C++ "std::type_identity" template class, which is defined by the C++
+  /// standard library.
+  LazyDeclPtr StdTypeIdentity;
+
   /// A flag to remember whether the implicit forms of operator new and delete
   /// have been declared.
   bool GlobalNewDeleteDeclared;
@@ -8127,7 +8144,7 @@ class Sema final : public SemaBase {
 
   /// The scope in which to find allocation functions.
   enum AllocationFunctionScope {
-    /// Only look for allocation functions in the global scope.
+    /// Only look for allocation functions in the global scope
     AFS_Global,
     /// Only look for allocation functions in the scope of the
     /// allocated class.
@@ -8139,14 +8156,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:
@@ -8177,11 +8192,11 @@ class Sema final : public SemaBase {
 
   bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                 DeclarationName Name, FunctionDecl *&Operator,
-                                bool Diagnose = true, bool WantSize = false,
-                                bool WantAligned = false);
-  FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
-                                              bool CanProvideSize,
-                                              bool Overaligned,
+                                QualType DeallocType,
+                                ImplicitDeallocationParameters,
+                                bool Diagnose = true);
+  FunctionDecl *FindUsualDeallocationFunction(QualType, SourceLocation StartLoc,
+                                              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 4552df2a2a31e6..f6a43bf5f493b7 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/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 9d0b77566f6747..e542e739912949 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8315,10 +8315,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/Decl.cpp b/clang/lib/AST/Decl.cpp
index cd173d17263792..c0d0336e1db4a3 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3358,6 +3358,12 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
     return false;
 
   const auto *proto = getType()->castAs<FunctionProtoType>();
+  if (proto->getNumParams() < 2)
+    return false;
+  bool IsTypeAwareAllocator =
+      proto->getParamType(0)->isTypeIdentitySpecialization();
+  if (IsTypeAwareAllocator)
+    return false;
   if (proto->getNumParams() != 2 || proto->isVariadic())
     return false;
 
@@ -3483,15 +3489,40 @@ bool FunctionDecl::isDestroyingOperatorDelete() const {
   //   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)
+  if (!isa<CXXMethodDecl>(this) || getOverloadedOperator() != OO_Delete)
+    return false;
+
+  auto NumParams = getNumParams();
+  unsigned DestroyingDeleteTagParam = 1;
+  bool IsTypeAware = false;
+  if (NumParams > 0)
+    IsTypeAware = getParamDecl(0)->getType()->isTypeIdentitySpecialization();
+
+  if (IsTypeAware)
+    ++DestroyingDeleteTagParam;
+
+  if (getNumParams() <= DestroyingDeleteTagParam)
     return false;
 
-  auto *RD = getParamDecl(1)->getType()->getAsCXXRecordDecl();
+  auto *RD =
+      getParamDecl(DestroyingDeleteTagParam)->getType()->getAsCXXRecordDecl();
   return RD && RD->isInStdNamespace() && RD->getIdentifier() &&
          RD->getIdentifier()->isStr("destroying_delete_t");
 }
 
+bool FunctionDecl::IsTypeAwareOperatorNewOrDelete() 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)
+    return false;
+  if (getNumParams() < 2)
+    return false;
+  return getParamDecl(0)->getType()->isTypeIdentitySpecialization();
+}
+
 LanguageLinkage FunctionDecl::getLanguageLinkage() const {
   return getDeclLanguageLinkage(*this);
 }
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 1c92fd9e3ff067..db3d884ec3ec50 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2474,11 +2474,39 @@ bool CXXMethodDecl::isUsualDeallocationFunction(
       getOverloadedOperator() != OO_Array_Delete)
     return false;
 
+  auto NumParams = getNumParams();
+  bool IsTypeAware = IsTypeAwareOperatorNewOrDelete();
+
   // C++ [basic.stc.dynamic.deallocation]p2:
   //   A template instance is never a usual deallocation function,
   //   regardless of its signature.
-  if (getPrimaryTemplate())
-    return false;
+  if (auto *PrimaryTemplate = getPrimaryTemplate()) {
+    // Addendum: a template instance is a usual deallocation function if there
+    // is a single template parameter, that parameter is a type, only the first
+    // parameter is dependent, and that parameter is a specialization of
+    // std::type_identity
+    if (!IsTypeAware) {
+      // Stop early on if the specialization is not explicitly type aware
+      return false;
+    }
+
+    auto *SpecializedDecl = PrimaryTemplate->getTemplatedDecl();
+    if (!SpecializedDecl->IsTypeAwareOperatorNewOrDelete()) {
+      // The underlying template decl must also be explicitly type aware
+      // e.g. template <typename T> void operator delete(T, ...)
+      // specialized on T=type_identity<X> is not usual
+      return false;
+    }
+
+    for (unsigned Idx = 1; Idx < NumParams; ++Idx) {
+      if (SpecializedDecl->getParamDecl(Idx)->getType()->isDependentType())
+        return false;
+    }
+  }
+
+  unsigned UsualParams = 1;
+  if (IsTypeAware)
+    ++UsualParams;
 
   // C++ [basic.stc.dynamic.deallocation]p2:
   //   If a class T has a member deallocation function named operator delete
@@ -2486,7 +2514,6 @@ bool CXXMethodDecl::isUsualDeallocationFunction(
   //   deallocation function. [...]
   if (getNumParams() == 1)
     return true;
-  unsigned UsualParams = 1;
 
   // C++ P0722:
   //   A destroying operator delete is a usual deallocation function if
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index a2c0c60d43dd14..7a9de7b9abd3b9 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -227,7 +227,8 @@ SourceLocation CXXScalarValueInitExpr::getBeginLoc() const {
 
 // CXXNewExpr
 CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
-                       FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
+                       FunctionDecl *OperatorDelete,
+                       ImplicitAllocationParameters IAP,
                        bool UsualArrayDeleteWantsSize,
                        ArrayRef<Expr *> PlacementArgs, SourceRange TypeIdParens,
                        std::optional<Expr *> ArraySize,
@@ -246,7 +247,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
 
   CXXNewExprBits.IsGlobalNew = IsGlobalNew;
   CXXNewExprBits.IsArray = ArraySize.has_value();
-  CXXNewExprBits.ShouldPassAlignment = ShouldPassAlignment;
+  CXXNewExprBits.ShouldPassAlignment = IAP.PassAlignment;
+  CXXNewExprBits.ShouldPassTypeIdentity = IAP.PassTypeIdentity;
   CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize;
   CXXNewExprBits.HasInitializer = Initializer != nullptr;
   CXXNewExprBits.StoredInitializationStyle =
@@ -291,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, ImplicitAllocationParameters IAP,
     bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
     SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
     CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -305,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/Type.cpp b/clang/lib/AST/Type.cpp
index 6bf2908e667c07..560b42e194a08c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3098,6 +3098,29 @@ bool Type::isStdByteType() const {
   return false;
 }
 
+TemplateDecl *Type::getSpecializedTemplateDecl() const {
+  const auto *DesugaredType = getUnqualifiedDesugaredType();
+  if (auto *Specialization = DesugaredType->getAs<TemplateSpecializationType>())
+    return Specialization->getTemplateName().getAsTemplateDecl();
+  if (const auto *Record = DesugaredType->getAsCXXRecordDecl()) {
+    if (auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Record))
+      return CTS->getSpecializedTemplate();
+  }
+  return nullptr;
+}
+
+bool Type::isTypeIdentitySpecialization() const {
+  const TemplateDecl *SpecializedDecl = getSpecializedTemplateDecl();
+  if (!SpecializedDecl)
+    return false;
+  IdentifierInfo *II = SpecializedDecl->getIdentifier();
+  if (!II)
+    return false;
+  if (!SpecializedDecl->isInStdNamespace())
+    return false;
+  return II->isStr("type_identity");
+}
+
 bool Type::isSpecifierType() const {
   // Note that this intentionally does not use the canonical type.
   switch (getTypeClass()) {
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 648b9b9ed98063..fa4695e0e4f382 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1380,6 +1380,7 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
 namespace {
 /// The parameters to pass to a usual operator delete.
 struct UsualDeleteParams {
+  bool TypedAwareDelete = false;
   bool DestroyingDelete = false;
   bool Size = false;
   bool Alignment = false;
@@ -1392,7 +1393,15 @@ 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()) {
+    // Assume Sema has ensured a non-pointer first parameter is
+    // a type identity
+    Params.TypedAwareDelete = true;
+    assert(AI != AE);
+    ++AI;
+  }
+
+  // The first non-type argument is always a void*.
   ++AI;
 
   // The next parameter may be a std::destroying_delete_t.
@@ -1435,6 +1444,8 @@ namespace {
     unsigned NumPlacementArgs : 31;
     LLVM_PREFERRED_TYPE(bool)
     unsigned PassAlignmentToPlacementDelete : 1;
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned PassTypeToPlacementDelete : 1;
     const FunctionDecl *OperatorDelete;
     ValueTy Ptr;
     ValueTy AllocSize;
@@ -1451,12 +1462,14 @@ namespace {
 
     CallDeleteDuringNew(size_t NumPlacementArgs,
                         const FunctionDecl *OperatorDelete, ValueTy Ptr,
-                        ValueTy AllocSize, bool PassAlignmentToPlacementDelete,
+                        ValueTy AllocSize, bool PassTypeToPlacementDelete,
+                        bool PassAlignmentToPlacementDelete,
                         CharUnits AllocAlign)
-      : NumPlacementArgs(NumPlacementArgs),
-        PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete),
-        OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
-        AllocAlign(AllocAlign) {}
+        : NumPlacementArgs(NumPlacementArgs),
+          PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete),
+          PassTypeToPlacementDelete(PassTypeToPlacementDelete),
+          OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
+          AllocAlign(AllocAlign) {}
 
     void setPlacementArg(unsigned I, RValueTy Arg, QualType Type) {
       assert(I < NumPlacementArgs && "index out of range");
@@ -1520,7 +1533,7 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
                                   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.
@@ -1536,7 +1549,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
 
     DirectCleanup *Cleanup = CGF.EHStack.pushCleanupWithExtra<DirectCleanup>(
         EHCleanup, E->getNumPlacementArgs(), E->getOperatorDelete(),
-        NewPtr.emitRawPointer(CGF), AllocSize, E->passAlignment(), AllocAlign);
+        NewPtr.emitRawPointer(CGF), AllocSize, E->passTypeIdentity(),
+        E->passAlignment(), AllocAlign);
     for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
       auto &Arg = NewArgs[I + NumNonPlacementArgs];
       Cleanup->setPlacementArg(I, Arg.getRValue(CGF), Arg.Ty);
@@ -1560,14 +1574,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(),
+          SavedNewPtr, SavedAllocSize, E->passTypeIdentity(),
+          E->passAlignment(), AllocAlign);
   for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
     auto &Arg = NewArgs[I + NumNonPlacementArgs];
     Cleanup->setPlacementArg(
@@ -1587,6 +1598,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);
@@ -1638,7 +1650,15 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
     const FunctionProtoType *allocatorType =
       allocator->getType()->castAs<FunctionProtoType>();
     unsigned ParamsToSkip = 0;
-
+    if (E->passTypeIdentity()) {
+      QualType SpecializedTypeIdentity = allocatorType->getParamType(0);
+      CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
+                                               SourceLocation());
+      allocatorArgs.add(EmitAnyExprToTemp(&TypeIdentityParam),
+                        SpecializedTypeIdentity);
+      ++ParamsToSkip;
+      ++IndexOfAlignArg;
+    }
     // The allocation size is the first argument.
     QualType sizeType = getContext().getSizeType();
     allocatorArgs.add(RValue::get(allocSize), sizeType);
@@ -1652,8 +1672,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
     // The allocation alignment may be passed as the second argument.
     if (E->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) &&
@@ -1809,6 +1829,15 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   auto Params = getUsualDeleteParams(DeleteFD);
   auto ParamTypeIt = DeleteFTy->param_type_begin();
 
+  llvm::AllocaInst *TypeIdentityag = nullptr;
+  if (Params.TypedAwareDelete) {
+    QualType SpecializedTypeIdentity = *ParamTypeIt++;
+    CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
+                                             SourceLocation());
+    DeleteArgs.add(EmitAnyExprToTemp(&TypeIdentityParam),
+                   SpecializedTypeIdentity);
+  }
+
   // Pass the pointer itself.
   QualType ArgTy = *ParamTypeIt++;
   DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
@@ -1861,6 +1890,9 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   // Emit the call to delete.
   EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs);
 
+  if (TypeIdentityag && TypeIdentityag->use_empty())
+    TypeIdentityag->eraseFromParent();
+
   // If call argument lowering didn't use the destroying_delete_t alloca,
   // remove it again.
   if (DestroyingDeleteTag && DestroyingDeleteTag->use_empty())
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index dca8d3fd7b3eaf..3ad2ec16688fb9 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7145,6 +7145,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
       types::isCXX(InputType))
     CmdArgs.push_back("-fcoro-aligned-allocation");
 
+  if (Args.hasFlag(options::OPT_fcxx_type_aware_allocators,
+                   options::OPT_fno_cxx_type_aware_allocators, false))
+    CmdArgs.push_back("-fexperimental-cxx-type-aware-allocators");
+
+  if (Args.hasFlag(options::OPT_fcxx_type_aware_destroying_delete,
+                   options::OPT_fno_cxx_type_aware_destroying_delete, false))
+    CmdArgs.push_back("-fcxx-type-aware-destroying-delete");
+
   Args.AddLastArg(CmdArgs, options::OPT_fdouble_square_bracket_attributes,
                   options::OPT_fno_double_square_bracket_attributes);
 
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 3724aaf804c905..595bdd27eea314 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1112,9 +1112,12 @@ 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 = {.PassTypeIdentity =
+                                            S.AllowTypeAwareAllocators(),
+                                        .PassAlignment = Overaligned,
+                                        .PassSize = true};
   if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
-                                 /*Diagnose*/ true, /*WantSize*/ true,
-                                 /*WantAligned*/ Overaligned))
+                                 PromiseType, IDP, /*Diagnose*/ true))
     return false;
 
   // [dcl.fct.def.coroutine]p12
@@ -1130,8 +1133,9 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
     // 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);
+    IDP.PassSize = CanProvideSize;
+    OperatorDelete =
+        S.FindUsualDeallocationFunction(PromiseType, Loc, IDP, DeleteName);
 
     if (!OperatorDelete)
       return false;
@@ -1421,29 +1425,36 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
 
   // Helper function to indicate whether the last lookup found the aligned
   // allocation function.
-  bool PassAlignment = S.getLangOpts().CoroAlignedAllocation;
-  auto LookupAllocationFunction = [&](Sema::AllocationFunctionScope NewScope =
-                                          Sema::AFS_Both,
-                                      bool WithoutPlacementArgs = false,
-                                      bool ForceNonAligned = false) {
-    // [dcl.fct.def.coroutine]p9
-    //   The allocation function's name is looked up by searching for it in the
-    // scope of the promise type.
-    // - If any declarations are found, ...
-    // - If no declarations are found in the scope of the promise type, a search
-    // is performed in the global scope.
-    if (NewScope == Sema::AFS_Both)
-      NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
-
-    PassAlignment = !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
-    FunctionDecl *UnusedResult = nullptr;
-    S.FindAllocationFunctions(Loc, SourceRange(), NewScope,
-                              /*DeleteScope*/ Sema::AFS_Both, PromiseType,
-                              /*isArray*/ false, PassAlignment,
-                              WithoutPlacementArgs ? MultiExprArg{}
-                                                   : PlacementArgs,
-                              OperatorNew, UnusedResult, /*Diagnose*/ false);
-  };
+  ImplicitAllocationParameters IAP = {
+      .PassTypeIdentity = S.AllowTypeAwareAllocators(),
+      .PassAlignment = S.getLangOpts().CoroAlignedAllocation != 0};
+  auto LookupAllocationFunction =
+      [&](Sema::AllocationFunctionScope NewScope = Sema::AFS_Both,
+          bool WithoutPlacementArgs = false, bool ForceNonAligned = false) {
+        // [dcl.fct.def.coroutine]p9
+        //   The allocation function's name is looked up by searching for it in
+        //   the
+        // scope of the promise type.
+        // - If any declarations are found, ...
+        // - If no declarations are found in the scope of the promise type, a
+        // search is performed in the global scope or via ADL for typed
+        // allocation.
+        if (NewScope == Sema::AFS_Both)
+          NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
+
+        IAP = {.PassTypeIdentity = S.AllowTypeAwareAllocators(),
+               .PassAlignment =
+                   !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation};
+
+        FunctionDecl *UnusedResult = nullptr;
+
+        S.FindAllocationFunctions(
+            Loc, SourceRange(), NewScope,
+            /*DeleteScope*/ Sema::AFS_Both, PromiseType,
+            /*isArray*/ false, IAP,
+            WithoutPlacementArgs ? MultiExprArg{} : PlacementArgs, OperatorNew,
+            UnusedResult, /*Diagnose*/ false);
+      };
 
   // We don't expect to call to global operator new with (size, p0, …, pn).
   // So if we choose to lookup the allocation function in global scope, we
@@ -1467,7 +1478,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     // 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))
+        (S.getLangOpts().CoroAlignedAllocation && !IAP.PassAlignment))
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
                                /*WithoutPlacementArgs*/ true);
   }
@@ -1492,7 +1503,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper variable to emit warnings.
   bool FoundNonAlignedInPromise = false;
   if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
-    if (!OperatorNew || !PassAlignment) {
+    if (!OperatorNew || !IAP.PassAlignment) {
       FoundNonAlignedInPromise = OperatorNew;
 
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
@@ -1586,8 +1597,22 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   if (NewRef.isInvalid())
     return false;
 
-  SmallVector<Expr *, 2> NewArgs(1, FrameSize);
-  if (S.getLangOpts().CoroAlignedAllocation && PassAlignment)
+  SmallVector<Expr *, 3> NewArgs;
+  if (IAP.PassTypeIdentity) {
+    auto SpecializedTypeIdentity =
+        S.InstantiateSpecializedTypeIdentity(PromiseType);
+    if (!SpecializedTypeIdentity)
+      return false;
+    auto *SpecializedTypeInfo =
+        S.Context.getTrivialTypeSourceInfo(*SpecializedTypeIdentity, Loc);
+    auto TypeIdentity =
+        S.BuildCXXTypeConstructExpr(SpecializedTypeInfo, Loc, {}, Loc, false);
+    if (TypeIdentity.isInvalid())
+      return false;
+    NewArgs.push_back(TypeIdentity.get());
+  }
+  NewArgs.push_back(FrameSize);
+  if (S.getLangOpts().CoroAlignedAllocation && 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 1aa3e8edfe1b13..1a7d45256559a1 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17401,6 +17401,19 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
     Previous.clear();
   }
 
+  // I think DC check should be DC->isStdNamespace()?
+  // Also these guards are questionable - it's possible to get incorrect
+  // codegen when the declared type does not match the expected type.
+  // e.g. something like
+  //    namespace std { struct align_val_t { explicit align_val_t(size_t); } };
+  // can result in an operator new/delete decl picking up this specified
+  // align_val_t struct, but the align_val_t constructed implicitly has the
+  // mismatching internal definition.
+  //
+  // The correct course of action is probably to perform this logic at the point
+  // a declaration is inserted into the DeclContext, where it can also perform
+  // appropriate type checks for warnings/errors, rather than working backwards
+  // from the assumption that the type will always be correct.
   if (getLangOpts().CPlusPlus && Name && DC && StdNamespace &&
       DC->Equals(getStdNamespace())) {
     if (Name->isStr("bad_alloc")) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1a691c0e1689d6..8c48ecf92eda48 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -47,6 +47,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"
@@ -9710,10 +9711,16 @@ 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;
+    auto DeallocType = Context.getRecordType(RD);
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
+    ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
+                                              AllowTypeAwareAllocators(),
+                                          .PassAlignment = false,
+                                          .PassSize = false};
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
-                                 OperatorDelete, /*Diagnose*/false)) {
+                                 OperatorDelete, DeallocType, IDP,
+                                 /*Diagnose*/ false)) {
       if (Diagnose)
         Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete);
       return true;
@@ -10864,14 +10871,19 @@ 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 PointerParam = 0;
+        if (IsTypeAwareOperatorNewOrDelete(OperatorDelete)) {
+          ++PointerParam;
+        }
+        QualType ParamType =
+            OperatorDelete->getParamDecl(PointerParam)->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(PointerParam)->getLocation());
           assert(!This.isInvalid() && "couldn't form 'this' expr in dtor?");
           This = PerformImplicitConversion(This.get(), ParamType,
                                            AssignmentAction::Passing);
@@ -11727,6 +11739,12 @@ NamespaceDecl *Sema::getStdNamespace() const {
   return cast_or_null<NamespaceDecl>(
                                  StdNamespace.get(Context.getExternalSource()));
 }
+
+ClassTemplateDecl *Sema::getStdTypeIdentity() const {
+  return cast_or_null<ClassTemplateDecl>(
+      StdTypeIdentity.get(Context.getExternalSource()));
+}
+
 namespace {
 
 enum UnsupportedSTLSelect {
@@ -16105,6 +16123,127 @@ bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
   return Invalid;
 }
 
+bool Sema::IsTypeIdentitySpecialization(QualType Type) const {
+  auto *TypeIdentity = getStdTypeIdentity();
+  if (!TypeIdentity)
+    return false;
+  auto *SpecializedDecl = Type->getSpecializedTemplateDecl();
+  return TypeIdentity == SpecializedDecl;
+}
+
+bool Sema::IsTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const {
+  // Type aware operators
+  if (FnDecl->getNumParams() < 2)
+    return false;
+  const auto *ParamDecl = FnDecl->getParamDecl(0);
+  return IsTypeIdentitySpecialization(ParamDecl->getType());
+}
+
+bool Sema::IsTypeAwareOperatorNewOrDelete(
+    const FunctionTemplateDecl *FTD) const {
+  return IsTypeAwareOperatorNewOrDelete(FTD->getTemplatedDecl());
+}
+
+bool Sema::IsTypeAwareOperatorNewOrDelete(const NamedDecl *ND) const {
+  if (auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+    return IsTypeAwareOperatorNewOrDelete(FTD->getTemplatedDecl());
+  if (auto *FnDecl = dyn_cast<FunctionDecl>(ND))
+    return IsTypeAwareOperatorNewOrDelete(FnDecl);
+  return false;
+}
+
+std::optional<FunctionDecl *>
+Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
+                                      QualType DeallocType) {
+  if (!AllowTypeAwareAllocators())
+    return std::nullopt;
+
+  auto TemplateParameters = FnTemplateDecl->getTemplateParameters();
+  if (TemplateParameters->hasParameterPack())
+    return std::nullopt;
+
+  auto FnDecl = FnTemplateDecl->getTemplatedDecl();
+  if (!IsTypeAwareOperatorNewOrDelete(FnDecl))
+    return std::nullopt;
+
+  if (FnDecl->isVariadic())
+    return std::nullopt;
+
+  unsigned NumParams = FnDecl->getNumParams();
+  if (NumParams < 2)
+    return std::nullopt;
+
+  for (size_t Idx = 1; Idx < NumParams; ++Idx) {
+    // A type aware allocation is only usual if the only dependent parameter is
+    // the first parameter
+    const auto *ParamDecl = FnDecl->getParamDecl(Idx);
+    if (ParamDecl->getType()->isDependentType())
+      return std::nullopt;
+  }
+
+  auto SpecializedTypeIdentity =
+      InstantiateSpecializedTypeIdentity(DeallocType);
+  if (!SpecializedTypeIdentity)
+    return std::nullopt;
+  SmallVector<QualType, 4> ArgTypes;
+  ArgTypes.reserve(NumParams);
+  ArgTypes.push_back(*SpecializedTypeIdentity);
+  ArgTypes.push_back(FnDecl->getParamDecl(1)->getType());
+  unsigned UsualParamsIdx = 2;
+  if (UsualParamsIdx < NumParams && FnDecl->isDestroyingOperatorDelete()) {
+    auto Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
+    ArgTypes.push_back(Type);
+    ++UsualParamsIdx;
+  }
+
+  if (UsualParamsIdx < NumParams) {
+    auto Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
+    if (Context.hasSameUnqualifiedType(Type, Context.getSizeType())) {
+      ArgTypes.push_back(Type);
+      ++UsualParamsIdx;
+    }
+  }
+
+  if (UsualParamsIdx < NumParams) {
+    auto Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
+    if (Type->isAlignValT()) {
+      ArgTypes.push_back(Type);
+      ++UsualParamsIdx;
+    }
+  }
+
+  if (UsualParamsIdx != NumParams)
+    return std::nullopt;
+
+  FunctionProtoType::ExtProtoInfo EPI;
+  auto ExpectedFunctionType =
+      Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
+  SourceLocation Loc;
+  sema::TemplateDeductionInfo Info(Loc);
+  FunctionDecl *Result;
+  if (DeduceTemplateArguments(FnTemplateDecl, nullptr, ExpectedFunctionType,
+                              Result, Info) != TemplateDeductionResult::Success)
+    return std::nullopt;
+  return Result;
+}
+
+std::optional<QualType>
+Sema::InstantiateSpecializedTypeIdentity(QualType Subject) {
+  assert(AllowTypeAwareAllocators());
+  auto *TypeIdentity = getStdTypeIdentity();
+  if (!TypeIdentity) {
+    return std::nullopt;
+  }
+  auto TN = TemplateName(TypeIdentity);
+  TemplateArgumentListInfo Arguments;
+  Arguments.addArgument(getTrivialTemplateArgumentLoc(
+      TemplateArgument(Subject), QualType(), SourceLocation()));
+  auto Result = CheckTemplateIdType(TN, SourceLocation(), Arguments);
+  if (Result.isNull())
+    return std::nullopt;
+  return Result;
+}
+
 static inline bool
 CheckOperatorNewDeleteDeclarationScope(Sema &SemaRef,
                                        const FunctionDecl *FnDecl) {
@@ -16134,32 +16273,31 @@ 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();
-
-  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 (auto ExpectedPtrTy = ExpectedResultType->getAs<PointerType>())
-      ExpectedResultType = RemoveAddressSpaceFromPtr(SemaRef, ExpectedPtrTy);
-  }
+static inline bool CheckOperatorNewDeleteTypes(
+    Sema &SemaRef, const FunctionDecl *FnDecl, CanQualType ExpectedResultType,
+    CanQualType ExpectedFirstParamType, unsigned DependentParamTypeDiag,
+    unsigned InvalidParamTypeDiag, unsigned *MinimumNonDefaultArgs) {
+  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);
+  };
+  auto *FnType = FnDecl->getType()->castAs<FunctionType>();
+  QualType CanResultType = NormalizeType(FnType->getReturnType());
+  auto CanExpectedResultType = NormalizeType(ExpectedResultType);
+  auto CanExpectedFirstParamType = NormalizeType(ExpectedFirstParamType);
 
   // 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;
@@ -16171,28 +16309,26 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
                       diag::err_operator_new_delete_template_too_few_parameters)
         << FnDecl->getDeclName();
 
+  unsigned FirstNonTypeParam = 0;
+  if (FnDecl->IsTypeAwareOperatorNewOrDelete()) {
+    if (!SemaRef.getLangOpts().TypeAwareAllocators) {
+      return SemaRef.Diag(FnDecl->getLocation(),
+                          diag::err_unsupported_type_aware_allocator);
+    }
+    ++FirstNonTypeParam;
+  }
+
   // The function decl must have at least 1 parameter.
-  if (FnDecl->getNumParams() == 0)
+  if (FnDecl->getNumParams() <= FirstNonTypeParam)
     return SemaRef.Diag(FnDecl->getLocation(),
                         diag::err_operator_new_delete_too_few_parameters)
       << FnDecl->getDeclName();
 
-  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);
-
-    if (auto ExpectedPtrTy = ExpectedFirstParamType->getAs<PointerType>())
-      ExpectedFirstParamType =
-          RemoveAddressSpaceFromPtr(SemaRef, ExpectedPtrTy);
-  }
+  QualType FirstParamType =
+      NormalizeType(FnDecl->getParamDecl(FirstNonTypeParam)->getType());
 
   // Check that the first parameter type is what we expect.
-  if (SemaRef.Context.getCanonicalType(FirstParamType).getUnqualifiedType() !=
-      ExpectedFirstParamType) {
+  if (FirstParamType.getUnqualifiedType() != CanExpectedFirstParamType) {
     // 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.
@@ -16202,6 +16338,7 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
            << FnDecl->getDeclName() << ExpectedFirstParamType;
   }
 
+  *MinimumNonDefaultArgs = FirstNonTypeParam + 1;
   return false;
 }
 
@@ -16217,22 +16354,26 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
   CanQualType SizeTy =
     SemaRef.Context.getCanonicalType(SemaRef.Context.getSizeType());
 
+  unsigned MinimumNonDefaultArgs = 0;
   // 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))
+  if (CheckOperatorNewDeleteTypes(
+          SemaRef, FnDecl, SemaRef.Context.VoidPtrTy, SizeTy,
+          diag::err_operator_new_dependent_param_type,
+          diag::err_operator_new_param_type, &MinimumNonDefaultArgs))
     return true;
-
+  assert(MinimumNonDefaultArgs > 0);
   // 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();
-
+  for (unsigned Idx = 0; Idx < MinimumNonDefaultArgs; ++Idx) {
+    auto *ParamDecl = FnDecl->getParamDecl(Idx);
+    if (ParamDecl->hasDefaultArg()) {
+      return SemaRef.Diag(FnDecl->getLocation(),
+                          diag::err_operator_new_default_arg)
+             << FnDecl->getDeclName() << ParamDecl->getDefaultArgRange();
+    }
+  }
   return false;
 }
 
@@ -16257,24 +16398,40 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
                 SemaRef.Context.getRecordType(MD->getParent())))
           : SemaRef.Context.VoidPtrTy;
 
+  unsigned MinimumNonDefaultArgs = 0;
   // C++ [basic.stc.dynamic.deallocation]p2:
   //   Each deallocation function shall return void
   if (CheckOperatorNewDeleteTypes(
           SemaRef, FnDecl, SemaRef.Context.VoidTy, ExpectedFirstParamType,
           diag::err_operator_delete_dependent_param_type,
-          diag::err_operator_delete_param_type))
+          diag::err_operator_delete_param_type, &MinimumNonDefaultArgs))
     return true;
 
+  assert(MinimumNonDefaultArgs > 0);
   // 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;
+    }
+    if (MD->IsTypeAwareOperatorNewOrDelete() &&
+        !SemaRef.getLangOpts().TypeAwareDestroyingDelete) {
+      SemaRef.Diag(MD->getLocation(),
+                   diag::err_type_aware_destroying_operator_delete);
+      return true;
+    }
+  }
+  for (unsigned Idx = 0; Idx < MinimumNonDefaultArgs; ++Idx) {
+    auto *ParamDecl = FnDecl->getParamDecl(Idx);
+    if (ParamDecl->hasDefaultArg()) {
+      return SemaRef.Diag(FnDecl->getLocation(),
+                          diag::err_operator_new_default_arg)
+             << FnDecl->getDeclName() << ParamDecl->getDefaultArgRange();
+    }
   }
-
   return false;
 }
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 50c1b24fce6da7..ad32debe0d24eb 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1754,6 +1754,9 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
     return false;
 
   unsigned UsualParams = 1;
+  if (S.AllowTypeAwareAllocators() && UsualParams < FD->getNumParams() &&
+      S.IsTypeAwareOperatorNewOrDelete(FD))
+    ++UsualParams;
 
   if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() &&
       S.Context.hasSameUnqualifiedType(
@@ -1773,14 +1776,40 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
 namespace {
   struct UsualDeallocFnInfo {
     UsualDeallocFnInfo() : Found(), FD(nullptr) {}
-    UsualDeallocFnInfo(Sema &S, DeclAccessPair Found)
+    UsualDeallocFnInfo(Sema &S, DeclAccessPair Found, QualType AllocType)
         : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
           Destroying(false), HasSizeT(false), HasAlignValT(false),
-          CUDAPref(SemaCUDA::CFP_Native) {
-      // A function template declaration is never a usual deallocation function.
-      if (!FD)
-        return;
+          HasTypeIdentity(false), CUDAPref(SemaCUDA::CFP_Native) {
+      // A function template declaration is only a usual deallocation function
+      // if it is a typed delete
+      if (!FD) {
+        auto *FTD = dyn_cast<FunctionTemplateDecl>(Found->getUnderlyingDecl());
+        if (!FTD)
+          return;
+        auto InstantiatedDecl =
+            S.InstantiateTypeAwareUsualDelete(FTD, AllocType);
+        if (!InstantiatedDecl)
+          return;
+        FD = *InstantiatedDecl;
+      }
       unsigned NumBaseParams = 1;
+      if (S.IsTypeAwareOperatorNewOrDelete(FD) &&
+          S.AllowTypeAwareAllocators()) {
+        auto TypeIdentityTag = FD->getParamDecl(0)->getType();
+        auto ExpectedTypeIdentityTag =
+            S.InstantiateSpecializedTypeIdentity(AllocType);
+        if (!ExpectedTypeIdentityTag) {
+          FD = nullptr;
+          return;
+        }
+        if (!S.Context.hasSameType(TypeIdentityTag, *ExpectedTypeIdentityTag)) {
+          FD = nullptr;
+          return;
+        }
+        HasTypeIdentity = true;
+        ++NumBaseParams;
+      }
+
       if (FD->isDestroyingOperatorDelete()) {
         Destroying = true;
         ++NumBaseParams;
@@ -1808,31 +1837,64 @@ 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 IDP) const {
       // C++ P0722:
       //   A destroying operator delete is preferred over a non-destroying
       //   operator delete.
       if (Destroying != Other.Destroying)
-        return Destroying;
+        return Destroying ? 1 : -1;
+
+      // Selection for type awareness has priority over alignment and size
+      if (HasTypeIdentity != Other.HasTypeIdentity)
+        return HasTypeIdentity == IDP.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;
+        return HasAlignValT == IDP.PassAlignment ? 1 : -1;
 
       if (HasSizeT != Other.HasSizeT)
-        return HasSizeT == WantSize;
+        return HasSizeT == IDP.PassSize ? 1 : -1;
+
+      if (HasTypeIdentity) {
+        // Type aware allocation involves templates so we need to choose
+        // the best type
+        auto *PrimaryTemplate = FD->getPrimaryTemplate();
+        auto *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 =
+              1 + Destroying + HasTypeIdentity + HasAlignValT + HasSizeT;
+          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, HasSizeT, HasAlignValT, HasTypeIdentity;
     SemaCUDA::CUDAFunctionPreference CUDAPref;
   };
 }
@@ -1847,15 +1909,41 @@ 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 (S.IsTypeAwareOperatorNewOrDelete(Operator)) {
+    auto 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, ImplicitDeallocationParameters IDP,
+    QualType DeallocType,
     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(), DeallocType);
     if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD) ||
         Info.CUDAPref == SemaCUDA::CFP_Never)
       continue;
@@ -1866,13 +1954,13 @@ static UsualDeallocFnInfo resolveDeallocationOverload(
         BestFns->push_back(Info);
       continue;
     }
-
-    if (Best.isBetterThan(Info, WantSize, WantAlign))
+    auto 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;
@@ -1888,7 +1976,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,
-                                         QualType allocType) {
+                                         bool PassType, QualType allocType) {
   const RecordType *record =
     allocType->getBaseElementTypeUnsafe()->getAs<RecordType>();
   if (!record) return false;
@@ -1913,9 +2001,11 @@ 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));
+  ImplicitDeallocationParameters IDP = {
+      .PassTypeIdentity = PassType,
+      .PassAlignment = hasNewExtendedAlignment(S, allocType),
+      .PassSize = false};
+  auto Best = resolveDeallocationOverload(S, ops, IDP, allocType);
   return Best && Best.HasSizeT;
 }
 
@@ -2328,8 +2418,10 @@ 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 = {
+      .PassTypeIdentity = AllowTypeAwareAllocators(),
+      .PassAlignment =
+          getLangOpts().AlignedAllocation && Alignment > NewAlignment};
 
   if (CheckArgsForPlaceholders(PlacementArgs))
     return ExprError();
@@ -2337,18 +2429,18 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
   AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both;
   if (!AllocType->isDependentType() &&
       !Expr::hasAnyTypeDependentArguments(PlacementArgs) &&
-      FindAllocationFunctions(
-          StartLoc, SourceRange(PlacementLParen, PlacementRParen), Scope, Scope,
-          AllocType, ArraySize.has_value(), PassAlignment, PlacementArgs,
-          OperatorNew, OperatorDelete))
+      FindAllocationFunctions(StartLoc,
+                              SourceRange(PlacementLParen, PlacementRParen),
+                              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) {
@@ -2360,7 +2452,13 @@ 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;
+    unsigned NumImplicitArgs = 1;
+    if (IAP.PassTypeIdentity) {
+      assert(OperatorNew->IsTypeAwareOperatorNewOrDelete());
+      NumImplicitArgs++;
+    }
+    if (IAP.PassAlignment)
+      NumImplicitArgs++;
     if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto,
                                NumImplicitArgs, PlacementArgs, AllPlaceArgs,
                                CallType))
@@ -2398,10 +2496,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.
@@ -2414,7 +2512,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());
@@ -2425,7 +2523,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     CallArgs.emplace_back(AllocationSize
                               ? static_cast<Expr *>(&AllocationSizeLiteral)
                               : &OpaqueAllocationSize);
-    if (PassAlignment)
+    if (IAP.PassAlignment)
       CallArgs.emplace_back(&DesiredAlignment);
     CallArgs.insert(CallArgs.end(), PlacementArgs.begin(), PlacementArgs.end());
 
@@ -2436,7 +2534,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() && !IAP.PassAlignment &&
         (OperatorNew->isImplicit() ||
          (OperatorNew->getBeginLoc().isValid() &&
           getSourceManager().isInSystemHeader(OperatorNew->getBeginLoc())))) {
@@ -2523,10 +2621,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,
@@ -2567,10 +2664,15 @@ 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,
+enum class ResolveMode { Typed, Untyped };
+static bool resolveAllocationOverloadInterior(
+    Sema &S, LookupResult &R, SourceRange Range, ResolveMode Mode,
+    SmallVectorImpl<Expr *> &Args, bool &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();
@@ -2578,6 +2680,8 @@ static bool resolveAllocationOverload(
     // Even member operator new/delete are implicitly treated as
     // static, so don't use AddMemberCandidate.
     NamedDecl *D = (*Alloc)->getUnderlyingDecl();
+    if (S.IsTypeAwareOperatorNewOrDelete(D) == (Mode != ResolveMode::Typed))
+      continue;
 
     if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
       S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(),
@@ -2613,11 +2717,11 @@ static bool resolveAllocationOverload(
     //   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);
+      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
@@ -2627,16 +2731,21 @@ 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 typed 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),
@@ -2660,16 +2769,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);
 
@@ -2709,31 +2823,110 @@ 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, RequireTyped };
+
+static void LookupGlobalDeallocationFunctions(Sema &S, SourceLocation Loc,
+                                              LookupResult &FoundDelete,
+                                              DeallocLookupMode Mode,
+                                              DeclarationName Name,
+                                              QualType DeallocType) {
+  S.LookupQualifiedName(FoundDelete, S.Context.getTranslationUnitDecl());
+  if (Mode == DeallocLookupMode::OptionallyTyped) {
+    bool RemoveTypedDecl = Mode == DeallocLookupMode::Untyped;
+    LookupResult::Filter Filter = FoundDelete.makeFilter();
+    while (Filter.hasNext()) {
+      auto Decl = Filter.next()->getUnderlyingDecl();
+      bool DeclIsTypeAware = S.IsTypeAwareOperatorNewOrDelete(Decl);
+      if (DeclIsTypeAware && 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 (IAP.PassTypeIdentity) {
+    assert(Args[0]->getType()->isTypeIdentitySpecialization());
+    SmallVector<Expr *> UntypedParameters;
+    UntypedParameters.reserve(Args.size() - 1);
+    UntypedParameters.append(Args.begin() + 1, Args.end());
+    bool InitialAlignmentMode = IAP.PassAlignment;
+    if (resolveAllocationOverloadInterior(
+            S, R, Range, ResolveMode::Typed, Args, IAP.PassAlignment, Operator,
+            AlignedCandidates, AlignArg, Diagnose))
+      return true;
+    if (Operator)
+      return false;
+    // There's no type aware allocator
+    IAP.PassTypeIdentity = false;
+    // Restore alignment requirements
+    IAP.PassAlignment = InitialAlignmentMode;
+    // Finally prepare the type free parameter list
+    Args = UntypedParameters;
+  }
+  assert(!Args[0]->getType()->isTypeIdentitySpecialization());
+  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
-  //    the global scope. Else, if AFS_Class, only look in the scope of the
-  //    allocated class. If AFS_Both, look in both.
+  //    the global scope or ADL associated namespaces. Else, if AFS_Class, only
+  //    look in the scope of the allocated class. If AFS_Both, look in both.
   // 2) If an array size is given, look for operator new[], else look for
   //   operator new.
   // 3) The first argument is always size_t. Append the arguments from the
   //   placement form.
 
   SmallVector<Expr*, 8> AllocArgs;
-  AllocArgs.reserve((PassAlignment ? 2 : 1) + PlaceArgs.size());
+  unsigned ImplicitArgCount = 1 + IAP.PassAlignment + IAP.PassTypeIdentity;
+  AllocArgs.reserve(ImplicitArgCount + 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 (IAP.PassTypeIdentity) {
+    if (auto SpecializedTypeIdentity =
+            InstantiateSpecializedTypeIdentity(AllocElemType)) {
+      TypeIdentity = *SpecializedTypeIdentity;
+    } else {
+      IAP.PassTypeIdentity = false;
+    }
+  }
+  bool OriginalTypeAwareState = IAP.PassTypeIdentity;
+
+  CXXScalarValueInitExpr TypeIdentityParam(TypeIdentity, nullptr, StartLoc);
+  if (IAP.PassTypeIdentity)
+    AllocArgs.push_back(&TypeIdentityParam);
+
   QualType SizeTy = Context.getSizeType();
   unsigned SizeTyWidth = Context.getTypeSize(SizeTy);
   IntegerLiteral Size(Context, llvm::APInt::getZero(SizeTyWidth), SizeTy,
@@ -2741,27 +2934,16 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   AllocArgs.push_back(&Size);
 
   QualType AlignValT = Context.VoidTy;
-  if (PassAlignment) {
+  if (IAP.PassAlignment) {
     DeclareGlobalNewDelete();
     AlignValT = Context.getTypeDeclType(getStdAlignValT());
   }
   CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
-  if (PassAlignment)
+  if (IAP.PassAlignment)
     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);
@@ -2785,10 +2967,8 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     if (R.empty()) {
       if (NewScope == AFS_Class)
         return true;
-
       LookupQualifiedName(R, Context.getTranslationUnitDecl());
     }
-
     if (getLangOpts().OpenCLCPlusPlus && R.empty()) {
       if (PlaceArgs.empty()) {
         Diag(StartLoc, diag::err_openclcxx_not_supported) << "default new";
@@ -2804,8 +2984,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;
   }
@@ -2863,7 +3043,11 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
       return true;
 
     DeclareGlobalNewDelete();
-    LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
+    auto LookupMode = OriginalTypeAwareState
+                          ? DeallocLookupMode::OptionallyTyped
+                          : DeallocLookupMode::Untyped;
+    LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
+                                      DeleteName, AllocElemType);
   }
 
   FoundDelete.suppressDiagnostics();
@@ -2883,7 +3067,12 @@ 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 (IAP.PassTypeIdentity)
+    ++NonPlacementNewArgCount;
+  bool isPlacementNew = !PlaceArgs.empty() ||
+                        OperatorNew->param_size() != NonPlacementNewArgCount ||
                         OperatorNew->isVariadic();
 
   if (isPlacementNew) {
@@ -2902,8 +3091,10 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
       auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
 
       SmallVector<QualType, 4> ArgTypes;
+      if (IAP.PassTypeIdentity)
+        ArgTypes.push_back(TypeIdentity);
       ArgTypes.push_back(Context.VoidPtrTy);
-      for (unsigned I = 1, N = Proto->getNumParams(); I < N; ++I)
+      for (unsigned I = ArgTypes.size(), N = Proto->getNumParams(); I < N; ++I)
         ArgTypes.push_back(Proto->getParamType(I));
 
       FunctionProtoType::ExtProtoInfo EPI;
@@ -2915,7 +3106,7 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
     }
 
     for (LookupResult::iterator D = FoundDelete.begin(),
-                             DEnd = FoundDelete.end();
+                                DEnd = FoundDelete.end();
          D != DEnd; ++D) {
       FunctionDecl *Fn = nullptr;
       if (FunctionTemplateDecl *FnTmpl =
@@ -2948,11 +3139,13 @@ 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 = {
+        .PassTypeIdentity = OriginalTypeAwareState,
+        .PassAlignment = hasNewExtendedAlignment(*this, AllocElemType),
+        .PassSize = FoundGlobalDelete};
     UsualDeallocFnInfo Selected = resolveDeallocationOverload(
-        *this, FoundDelete, /*WantSize*/ FoundGlobalDelete,
-        /*WantAlign*/ hasNewExtendedAlignment(*this, AllocElemType),
-        &BestDeallocFns);
-    if (Selected)
+        *this, FoundDelete, IDP, AllocElemType, &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
@@ -2968,6 +3161,29 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
   //   deallocation function will be called.
   if (Matches.size() == 1) {
     OperatorDelete = Matches[0].second;
+    if (IsTypeAwareOperatorNewOrDelete(OperatorDelete) !=
+        IAP.PassTypeIdentity) {
+      Diag(StartLoc, diag::warn_mismatching_type_aware_cleanup_deallocator);
+      int NewDiagIndex = IsTypeAwareOperatorNewOrDelete(OperatorNew) ? 0 : 1;
+      int DeleteDiagIndex =
+          IsTypeAwareOperatorNewOrDelete(OperatorDelete) ? 0 : 1;
+      Diag(OperatorNew->getLocation(), diag::note_type_aware_operator_declared)
+          << NewDiagIndex << OperatorNew->getDeclName();
+      Diag(OperatorDelete->getLocation(),
+           diag::note_type_aware_operator_declared)
+          << DeleteDiagIndex << OperatorDelete->getDeclName();
+    }
+    if (IAP.PassTypeIdentity &&
+        OperatorDelete->getDeclContext() != OperatorNew->getDeclContext()) {
+      Diag(StartLoc,
+           diag::err_no_matching_type_aware_cleanup_deallocator_mismatch)
+          << OperatorNew->getDeclName() << DeleteName
+          << OperatorNew->getDeclContext();
+      Diag(OperatorNew->getLocation(), diag::err_type_aware_operator_found)
+          << OperatorNew->getDeclName() << OperatorNew->getDeclContext();
+      Diag(OperatorDelete->getLocation(), diag::err_type_aware_operator_found)
+          << OperatorDelete->getDeclName() << OperatorDelete->getDeclContext();
+    }
 
     // C++1z [expr.new]p23:
     //   If the lookup finds a usual deallocation function (3.7.4.2)
@@ -2978,16 +3194,20 @@ 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);
       // 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;
       if (IsSizedDelete && !FoundGlobalDelete) {
-        auto NonSizedDelete =
-            resolveDeallocationOverload(*this, FoundDelete, /*WantSize*/false,
-                                        /*WantAlign*/Info.HasAlignValT);
+        ImplicitDeallocationParameters SizeTestingIDP = {
+            .PassTypeIdentity = Info.HasTypeIdentity,
+            .PassAlignment = Info.HasAlignValT,
+            .PassSize = false};
+        auto NonSizedDelete = resolveDeallocationOverload(
+            *this, FoundDelete, SizeTestingIDP, AllocElemType);
         if (NonSizedDelete && !NonSizedDelete.HasSizeT &&
             NonSizedDelete.HasAlignValT == Info.HasAlignValT)
           IsSizedDelete = false;
@@ -3004,9 +3224,11 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
               << DeleteName;
       }
     }
+    if (CheckDeleteOperator(*this, StartLoc, Range, /* Diagnose */ true,
+                            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.
@@ -3267,21 +3489,32 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
   }
 }
 
-FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
-                                                  bool CanProvideSize,
-                                                  bool Overaligned,
-                                                  DeclarationName Name) {
+FunctionDecl *Sema::FindUsualDeallocationFunction(
+    QualType DeallocType, SourceLocation StartLoc,
+    ImplicitDeallocationParameters IDP, DeclarationName Name) {
   DeclareGlobalNewDelete();
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
-  LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
+  auto LookupMode = AllowTypeAwareAllocators()
+                        ? DeallocLookupMode::OptionallyTyped
+                        : DeallocLookupMode::Untyped;
+  LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
+                                    Name, DeallocType);
 
   // 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, DeallocType);
+  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;
 }
@@ -3291,22 +3524,32 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
   DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete);
 
   FunctionDecl *OperatorDelete = nullptr;
-  if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete))
+  auto DeallocType = Context.getRecordType(RD);
+  ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
+                                            AllowTypeAwareAllocators(),
+                                        .PassAlignment = false,
+                                        .PassSize = false};
+
+  if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, DeallocType, IDP))
     return nullptr;
+
   if (OperatorDelete)
     return OperatorDelete;
 
   // If there's no class-specific operator delete, look up the global
   // non-array delete.
-  return FindUsualDeallocationFunction(
-      Loc, true, hasNewExtendedAlignment(*this, Context.getRecordType(RD)),
-      Name);
+  auto RecordType = Context.getRecordType(RD);
+  IDP.PassAlignment = hasNewExtendedAlignment(*this, RecordType);
+  IDP.PassSize = true;
+  return FindUsualDeallocationFunction(RecordType, Loc, IDP, Name);
 }
 
 bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                     DeclarationName Name,
-                                    FunctionDecl *&Operator, bool Diagnose,
-                                    bool WantSize, bool WantAligned) {
+                                    FunctionDecl *&Operator,
+                                    QualType DeallocType,
+                                    ImplicitDeallocationParameters IDP,
+                                    bool Diagnose) {
   LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
   // Try to find operator delete/operator delete[] in class scope.
   LookupQualifiedName(Found, RD);
@@ -3316,36 +3559,21 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
 
   Found.suppressDiagnostics();
 
-  bool Overaligned =
-      WantAligned || hasNewExtendedAlignment(*this, Context.getRecordType(RD));
+  IDP.PassAlignment |=
+      hasNewExtendedAlignment(*this, Context.getRecordType(RD));
 
   // 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, DeallocType, &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.
@@ -3768,9 +3996,13 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
                                       ArrayForm ? OO_Array_Delete : OO_Delete);
 
     if (PointeeRD) {
+      ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
+                                                AllowTypeAwareAllocators(),
+                                            .PassAlignment = false,
+                                            .PassSize = false};
       if (!UseGlobal &&
           FindDeallocationFunction(StartLoc, PointeeRD, DeleteName,
-                                   OperatorDelete))
+                                   OperatorDelete, Pointee, IDP))
         return ExprError();
 
       // If we're allocating an array of records, check whether the
@@ -3779,16 +4011,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;
+              UsualDeallocFnInfo(
+                  *this, DeclAccessPair::make(OperatorDelete, AS_public),
+                  Pointee)
+                  .HasSizeT;
       }
 
       if (!PointeeRD->hasIrrelevantDestructor())
@@ -3818,8 +4051,14 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
       bool Overaligned = hasNewExtendedAlignment(*this, Pointee);
 
       // Look for a global declaration.
-      OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize,
-                                                     Overaligned, DeleteName);
+      ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
+                                                AllowTypeAwareAllocators(),
+                                            .PassAlignment = Overaligned,
+                                            .PassSize = CanProvideSize};
+      OperatorDelete =
+          FindUsualDeallocationFunction(Pointee, StartLoc, IDP, DeleteName);
+      if (!OperatorDelete)
+        return ExprError();
     }
 
     if (OperatorDelete->isInvalidDecl())
@@ -3844,7 +4083,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
     // 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();
+    unsigned PointeeIndex = 0;
+    if (IsTypeAwareOperatorNewOrDelete(OperatorDelete))
+      PointeeIndex = 1;
+    QualType ParamType = OperatorDelete->getParamDecl(PointeeIndex)->getType();
     if (!IsVirtualDelete && !ParamType->getPointeeType()->isVoidType()) {
       Qualifiers Qs = Pointee.getQualifiers();
       if (Qs.hasCVRQualifiers()) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4503e60cff8c2f..ba9edefc44a048 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1876,6 +1876,14 @@ DeclResult Sema::CheckClassTemplate(
   if (Previous.isAmbiguous())
     return true;
 
+  bool isStdTypeIdentity = false;
+  // Handle redeclaration of std::type_identity
+  if (Name && CurContext->isStdNamespace() && Name->isStr("type_identity")) {
+    if (Previous.empty() && StdTypeIdentity)
+      Previous.addDecl(getStdTypeIdentity());
+    isStdTypeIdentity = true;
+  }
+
   // Let the template parameter scope enter the lookup chain of the current
   // class template. For example, given
   //
@@ -2057,6 +2065,10 @@ DeclResult Sema::CheckClassTemplate(
   if (ShouldAddRedecl)
     NewTemplate->setPreviousDecl(PrevClassTemplate);
 
+  if (isStdTypeIdentity &&
+      (!StdTypeIdentity || getStdTypeIdentity()->isImplicit()))
+    StdTypeIdentity = NewTemplate;
+
   NewClass->setDescribedClassTemplate(NewTemplate);
 
   if (ModulePrivateLoc.isValid())
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 6aaafb2e8d71cc..15a85b6f0b8336 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1915,6 +1915,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 321e0031661ee2..e7372b51f397c3 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1919,6 +1919,7 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
 
   Record.push_back(E->isGlobalNew());
   Record.push_back(E->passAlignment());
+  Record.push_back(E->passTypeIdentity());
   Record.push_back(E->doesUsualArrayDeleteWantSize());
   Record.push_back(E->CXXNewExprBits.HasInitializer);
   Record.push_back(E->CXXNewExprBits.StoredInitializationStyle);
diff --git a/clang/test/CodeGenCXX/new.cpp b/clang/test/CodeGenCXX/new.cpp
index af225529c494e6..a0eff4b612980c 100644
--- a/clang/test/CodeGenCXX/new.cpp
+++ b/clang/test/CodeGenCXX/new.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -emit-llvm -fexperimental-cxx-type-aware-allocators -o - | FileCheck %s
 
 typedef __typeof__(sizeof(0)) size_t;
 
diff --git a/clang/test/CodeGenCoroutines/coro-alloc-2.cpp b/clang/test/CodeGenCoroutines/coro-alloc-2.cpp
index 9c60c32a5c5440..b0a56597a3a7bd 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++20 -triple x86_64 -emit-llvm -disable-llvm-passes %s -fexperimental-cxx-type-aware-allocators -o - | FileCheck %s
+
 #include "Inputs/coroutine.h"
 
 namespace std {
diff --git a/clang/test/CodeGenCoroutines/coro-alloc.cpp b/clang/test/CodeGenCoroutines/coro-alloc.cpp
index 7b3be7e0b7f98d..2409610e519a2e 100644
--- a/clang/test/CodeGenCoroutines/coro-alloc.cpp
+++ b/clang/test/CodeGenCoroutines/coro-alloc.cpp
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O2 \
 // RUN:    -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes \
 // RUN:   | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O2 \
+// RUN:    -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes -fexperimental-cxx-type-aware-allocators \
+// RUN:   | FileCheck %s
 
 namespace std {
 template <typename... T>
diff --git a/clang/test/Modules/new-delete.cpp b/clang/test/Modules/new-delete.cpp
index 585a242b22474e..47b4a06fc97ed6 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 -fexperimental-cxx-type-aware-allocators -verify %s
 // expected-no-diagnostics
 
 #pragma clang module build M
diff --git a/clang/test/SemaCXX/coroutine-alloc-4.cpp b/clang/test/SemaCXX/coroutine-alloc-4.cpp
index 262c163fb17897..c084ce64a59b1c 100644
--- a/clang/test/SemaCXX/coroutine-alloc-4.cpp
+++ b/clang/test/SemaCXX/coroutine-alloc-4.cpp
@@ -1,11 +1,18 @@
 // Tests that we'll find aligned allocation function properly.
 // RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify -fcoro-aligned-allocation
+// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify -fcoro-aligned-allocation -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify -fcoro-aligned-allocation -fexperimental-cxx-type-aware-allocators -DUSE_TAA
 
 #include "Inputs/std-coroutine.h"
 
 namespace std {
     typedef __SIZE_TYPE__ size_t;
     enum class align_val_t : size_t {};
+    #ifdef USE_TAA
+    template <typename T> struct type_identity {
+      typedef T type;
+    };
+    #endif
 }
 
 struct task {
@@ -15,7 +22,12 @@ struct task {
     auto get_return_object() { return task{}; }
     void unhandled_exception() {}
     void return_value(int) {}
+    #ifdef USE_TAA
+    template <typename T>
+    void *operator new(std::type_identity<T>, std::size_t); // expected-warning 1+{{under -fcoro-aligned-allocation, the non-aligned allocation function for the promise type 'f' has higher precedence than the global aligned allocation function}}
+    #else
     void *operator new(std::size_t); // expected-warning 1+{{under -fcoro-aligned-allocation, the non-aligned allocation function for the promise type 'f' has higher precedence than the global aligned allocation function}}
+    #endif
   };
 };
 
@@ -30,7 +42,12 @@ struct task2 {
     auto get_return_object() { return task2{}; }
     void unhandled_exception() {}
     void return_value(int) {}
+    #ifdef USE_TAA
+    template <typename T>
+    void *operator new(std::type_identity<T>, std::size_t, std::align_val_t);
+    #else
     void *operator new(std::size_t, std::align_val_t);
+    #endif
   };
 };
 
@@ -64,7 +81,12 @@ struct task4 {
     auto get_return_object() { return task4{}; }
     void unhandled_exception() {}
     void return_value(int) {}
+    #ifdef USE_TAA
+    template <typename T>
+    void *operator new(std::type_identity<T>, std::size_t, std::align_val_t, int, double, int) noexcept;
+    #else
     void *operator new(std::size_t, std::align_val_t, int, double, int) noexcept;
+    #endif
   };
 };
 
@@ -109,7 +131,12 @@ task6 f5() { // expected-error 1+{{unable to find '::operator new(size_t, align_
     co_return 43;
 }
 
-void *operator new(std::size_t, std::align_val_t, std::nothrow_t) noexcept; 
+#ifdef USE_TAA
+template <typename T>
+void *operator new(std::type_identity<T>, std::size_t, std::align_val_t, std::nothrow_t) noexcept;
+#else
+void *operator new(std::size_t, std::align_val_t, std::nothrow_t) noexcept;
+#endif
 
 task6 f6() {
     co_return 43;
diff --git a/clang/test/SemaCXX/coroutine-allocs.cpp b/clang/test/SemaCXX/coroutine-allocs.cpp
index c9797208e2dc58..2a898eef06dfaa 100644
--- a/clang/test/SemaCXX/coroutine-allocs.cpp
+++ b/clang/test/SemaCXX/coroutine-allocs.cpp
@@ -1,7 +1,15 @@
 // RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify -fexperimental-cxx-type-aware-allocators -DUSE_TAA
+
 #include "Inputs/std-coroutine.h"
 
 namespace std {
+#ifdef USE_TAA
+template <typename T> struct type_identity {
+  typedef T type;
+};
+#endif
 typedef decltype(sizeof(int)) size_t;
 }
 
@@ -9,7 +17,11 @@ struct Allocator {};
 
 struct resumable {
   struct promise_type {
+    #ifdef USE_TAA
+    template <typename T> void *operator new(std::type_identity<T>, std::size_t sz, Allocator &);
+    #else
     void *operator new(std::size_t sz, Allocator &);
+    #endif
 
     resumable get_return_object() { return {}; }
     auto initial_suspend() { return std::suspend_always(); }
diff --git a/clang/test/SemaCXX/delete.cpp b/clang/test/SemaCXX/delete.cpp
index 7d1f51cb218ceb..787615e52fb013 100644
--- a/clang/test/SemaCXX/delete.cpp
+++ b/clang/test/SemaCXX/delete.cpp
@@ -1,10 +1,15 @@
 // Test without PCH
 // RUN: %clang_cc1 -fsyntax-only -include %S/delete-mismatch.h -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 -fexperimental-new-constant-interpreter | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-cxx-type-aware-allocators -include %S/delete-mismatch.h -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 -fexperimental-new-constant-interpreter | FileCheck %s
 
 // Test with PCH
 // 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/type-aware-new-constexpr.cpp b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
new file mode 100644
index 00000000000000..962d03c6839adb
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions 
+
+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);
+// expected-note at -1 {{candidate function not viable: no known conversion from 'type_identity<S2>' to 'type_identity<S1>' for 1st argument}}
+void operator delete(std::type_identity<S1>, void* ptr);
+
+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) = delete;
+// expected-note at -1 {{candidate function has been explicitly deleted}}
+void operator delete(std::type_identity<S2>, void* ptr) = delete;
+// expected-note at -1 {{'operator delete' has been explicitly marked deleted here}}
+
+constexpr int ensure_constexpr_retains_types_at_runtime() {
+  // Verify we dont resolve typed allocators in const contexts
+  S2 *s = new S2(); // expected-error {{call to deleted function 'operator new'}}
+  auto result = s->i;
+  delete s; // expected-error {{attempt to use a deleted function}}
+  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) = delete;
+  // expected-note at -1 {{candidate function [with T = S3] has been explicitly deleted}}
+  template <typename T> void operator delete(std::type_identity<T>, void *) = delete;
+  // expected-note at -1 {{'operator delete<S3>' has been explicitly marked deleted here}}
+};
+
+template <typename T> void* operator new(std::type_identity<T>, size_t sz) = delete;
+template <typename T> void operator delete(std::type_identity<T>, void *) = delete;
+
+constexpr int constexpr_vs_inclass_operators() {
+  S3 *s;
+  if consteval {
+    s = ::new S3();
+  } else {
+    s = new S3(); // expected-error {{call to deleted function 'operator new'}}
+  }
+  auto result = s->i;
+  if consteval {
+    ::delete s;
+  } else {
+    delete s; // expected-error {{attempt to use a deleted function}}
+  }
+  return result;
+};
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 00000000000000..d46bc7bd845c60
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions -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) = delete;
+   // expected-note at -1 {{candidate function [with T = BasicTypeAwareArrayAllocator] has been explicitly deleted}}
+   template <typename T> void  operator delete[](std::type_identity<T>, void*) = delete;
+   // expected-note at -1 {{'operator delete[]<BasicTypeAwareArrayAllocator>' has been explicitly marked deleted here}}
+};
+void *operator new[](std::type_identity<BasicTypeAwareArrayAllocator>, size_t);
+void  operator delete[](std::type_identity<BasicTypeAwareArrayAllocator>, void*);
+
+struct BasicTypeAwareNonArrayAllocator {
+   template <typename T> void *operator new[](std::type_identity<T>, size_t);
+   template <typename T> void  operator delete[](std::type_identity<T>, void*);
+   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);
+   template <typename T> void  operator delete[](std::type_identity<T>, void*);
+};
+
+void *operator new[](std::type_identity<WorkingTypeAwareAllocator>, size_t) = delete;
+void  operator delete[](std::type_identity<WorkingTypeAwareAllocator>, void*) = delete;
+
+
+void test() {
+  BasicTypeAwareArrayAllocator *A0 = new BasicTypeAwareArrayAllocator[10]; // expected-error {{call to deleted function 'operator new[]'}}
+  delete [] A0; // expected-error {{attempt to use a deleted function}}
+  
+  BasicTypeAwareNonArrayAllocator *A1 = new BasicTypeAwareNonArrayAllocator[10]; // ex
+  delete [] A1;
+
+  WorkingTypeAwareAllocator *A2 = new WorkingTypeAwareAllocator[10]; // expected-note {{allocated with 'new[]' here}}
+  delete A2; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+
+  WorkingTypeAwareAllocator *A3 = new WorkingTypeAwareAllocator; // expected-note {{allocated with 'new' here}}
+  delete [] A3; // expected-warning {{'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 00000000000000..e0dc0612a16def
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
@@ -0,0 +1,95 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s          -std=c++17 -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TAA -std=c++17 -fno-experimental-cxx-type-aware-allocators
+
+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);
+void *operator new(std::type_identity<int>, size_t, std::align_val_t);
+void *operator new(std::type_identity<int>, size_t, TestType&);
+template <typename T> void *operator new(std::type_identity<T>, size_t);
+template <typename T> void *operator new(std::type_identity<T>, size_t, TestType&);
+template <typename T> void *operator new(std::type_identity<TemplateTestType<T>>, size_t, TestType&);
+template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, TemplateTestType<U>&);
+template <template <typename> class T> void *operator new(std::type_identity<T<int>>, size_t); 
+#if defined(NO_TAA)
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error at -9 {{type aware allocation operators are disabled}}
+#endif
+
+void operator delete(std::type_identity<int>, void *);
+void operator delete(std::type_identity<int>, void *, std::align_val_t);
+void operator delete(std::type_identity<int>, void *, size_t);
+void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t);
+#if defined(NO_TAA)
+//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error at -5 {{type aware allocation operators are disabled}}
+#endif
+
+template <typename T> void operator delete(std::type_identity<T>, void *);
+template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t);
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t);
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
+#if defined(NO_TAA)
+//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error at -5 {{type aware allocation operators are disabled}}
+#endif
+
+template <typename T> void operator delete(std::type_identity<TemplateTestType<T>>, void *);
+template <template <typename> class T> void operator delete(std::type_identity<T<int>>, void *);
+#if defined(NO_TAA)
+//expected-error at -3 {{type aware allocation operators are disabled}}
+//expected-error at -3 {{type aware allocation operators are disabled}}
+#endif
+
+typedef std::type_identity<float> TypeIdentityAlias1;
+void *operator new(TypeIdentityAlias1, size_t);
+#if defined(NO_TAA)
+//expected-error at -2 {{type aware allocation operators are disabled}}
+#endif
+
+using TypeIdentityAlias2 = std::type_identity<double>;
+void *operator new(TypeIdentityAlias2, size_t);
+#if defined(NO_TAA)
+//expected-error at -2 {{type aware allocation operators are disabled}}
+#endif
+
+template <typename T> using TypeIdentityAlias3 = std::type_identity<T>;
+template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t);
+#if defined(NO_TAA)
+//expected-error at -2 {{type aware allocation operators are disabled}}
+#endif
+
+
+// Invalid free declarations - need to update error text
+template <typename T> void *operator new(T, size_t);
+// expected-error at -1 {{'operator new' cannot take a dependent type as first parameter; use size_t ('unsigned long') instead}}
+
+template <typename T> void operator delete(T, void*);
+// expected-error at -1 {{'operator delete' cannot take a dependent type as first parameter; use 'void *' instead}}
+
+template <typename T> struct S {
+  typedef std::type_identity<T> type_identity;
+};
+
+template <typename T> void *operator new(typename S<T>::type_identity, size_t);
+// expected-error at -1 {{'operator new' cannot take a dependent type as first parameter; use size_t ('unsigned long') instead}}
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 00000000000000..a80a7cc5a9f3a6
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++17    -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 -fsyntax-only -verify %s           -std=c++17    -fexperimental-cxx-type-aware-allocators -fcxx-type-aware-destroying-delete
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TAA  -std=c++17 -fno-experimental-cxx-type-aware-allocators
+
+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);
+  void operator delete(std::type_identity<S>, void *);
+#if defined(NO_TAA)
+  //expected-error at -3 {{type aware allocation operators are disabled}}
+  //expected-error at -3 {{type aware allocation operators are disabled}}
+#endif
+  void operator delete(S *, std::destroying_delete_t);
+};
+
+template <typename T> struct S2 {
+  void *operator new(std::type_identity<S2<T>>, size_t);
+  void operator delete(std::type_identity<S2<T>>, void *);
+#if defined(NO_TAA)
+  //expected-error at -3 {{type aware allocation operators are disabled}}
+  //expected-error at -3 {{type aware allocation operators are disabled}}
+#endif
+  void operator delete(S2 *, std::destroying_delete_t);
+};
+
+struct S3 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  template <typename T> void operator delete(std::type_identity<T>, void *);
+#if defined(NO_TAA)
+  //expected-error at -3 {{type aware allocation operators are disabled}}
+  //expected-error at -3 {{type aware allocation operators are disabled}}
+#endif
+  void operator delete(S3 *, std::destroying_delete_t);
+};
+
+struct S4 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  template <typename T> void operator delete(std::type_identity<T>, void *);
+  template <typename T> void operator delete(std::type_identity<T>, S4 *, std::destroying_delete_t); // #1
+#if defined(NO_TAA)
+  //expected-error at -4 {{type aware allocation operators are disabled}}
+  //expected-error at -4 {{type aware allocation operators are disabled}}
+  //expected-error at -4 {{type aware allocation operators are disabled}}
+#elif defined(NO_TADD)
+  // expected-error@#1 {{type aware destroying delete is not permitted}}
+#endif
+};
+
+struct S5 {
+  template <typename T> void operator delete(std::type_identity<T>, T *); // #2
+#if defined(NO_TAA)
+  // expected-error@#2 {{type aware allocation operators are disabled}}
+#else
+  // expected-error@#2 {{'operator delete' cannot take a dependent type as first parameter; use 'void *'}}
+#endif
+};
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 00000000000000..62f1672fd90b32
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -0,0 +1,302 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DTADD -std=c++2c -fexperimental-cxx-type-aware-allocators -fcxx-type-aware-destroying-delete -fexceptions
+
+namespace std {
+  template <class T> struct type_identity {};
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+static_assert(__has_feature(cxx_type_aware_allocators));
+#ifdef TADD
+static_assert(__has_feature(cxx_type_aware_destroying_delete));
+#else
+static_assert(!__has_feature(cxx_type_aware_destroying_delete));
+#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; // expected-note {{candidate function has been explicitly deleted}}
+  void  operator delete(void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<UntypedInclassNew>, size_t); // expected-note {{candidate function not viable}}
+void  operator delete(std::type_identity<UntypedInclassNew>, void*);
+
+struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_NoAlignedAlloc {
+  void *operator new(size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+  void  operator delete(void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, size_t, std::align_val_t); // expected-note {{candidate function not viable}}
+void operator delete(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, void *, std::align_val_t);
+
+struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_AlignedAlloc {
+  void *operator new(size_t, std::align_val_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+  void  operator delete(void *, std::align_val_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, size_t, std::align_val_t); // expected-note {{candidate function not viable}}
+void  operator delete(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, void *, std::align_val_t);
+
+struct BasicClass {};
+void *operator new(std::type_identity<BasicClass>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+void  operator delete(std::type_identity<BasicClass>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+
+struct InclassNew1 {
+  void *operator new(std::type_identity<InclassNew1>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+  void  operator delete(std::type_identity<InclassNew1>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<InclassNew1>, size_t); // expected-note {{candidate function not viable}}
+void  operator delete(std::type_identity<InclassNew1>, void *);
+
+struct InclassNew2 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t) = delete; // expected-note {{candidate function [with T = InclassNew2] has been explicitly deleted}}
+  template <typename T> void  operator delete(std::type_identity<T>, void *) = delete; // expected-note {{'operator delete<InclassNew2>' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<InclassNew2>, size_t); // expected-note {{candidate function not viable}}
+void  operator delete(std::type_identity<InclassNew2>, void *);
+
+struct InclassNew3 {
+  void *operator new(std::type_identity<InclassNew3>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+  void  operator delete(std::type_identity<InclassNew3>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // expected-note {{candidate function [with T = InclassNew3]}}
+  template <typename T> void  operator delete(std::type_identity<T>, void *);
+};
+
+struct __attribute__((aligned(128))) InclassNew4 {
+  void *operator new(std::type_identity<InclassNew4>, size_t); // expected-note {{candidate function not viable}}
+  void  operator delete(std::type_identity<InclassNew4>, void *);
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // expected-note {{candidate function [with T = InclassNew4] has been explicitly deleted}}
+  template <typename T> void  operator delete(std::type_identity<T>, void *, std::align_val_t) = delete; // expected-note {{'operator delete<InclassNew4>' has been explicitly marked deleted here}}
+};
+
+struct InclassNew5 {
+  InclassNew5();
+  void *operator new(std::type_identity<InclassNew5>, size_t);
+  void  operator delete(void *);
+  void  operator delete(std::type_identity<InclassNew5>, void *) = delete; // expected-note 2 {{'operator delete' has been explicitly marked deleted here}}
+};
+
+struct InclassNew6 {
+  InclassNew6();
+  void *operator new(size_t); // expected-note {{non-type aware 'operator new' declared here}}
+  void  operator delete(void *) = delete;
+  void  operator delete(std::type_identity<InclassNew6>, void *) = delete; // expected-note 2 {{'operator delete' has been explicitly marked deleted here}}
+  // expected-note at -1 {{type aware 'operator delete' declared here}}
+};
+
+struct InclassNew7 {
+  InclassNew7();
+  void *operator new(std::type_identity<InclassNew7>, size_t);
+  void  operator delete(std::type_identity<InclassNew7>, void *);
+  void  operator delete(InclassNew7 *, std::destroying_delete_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+
+struct InclassNew8 {
+  InclassNew8();
+  void *operator new(std::type_identity<InclassNew8>, size_t);  // expected-note {{type aware 'operator new' declared here}}
+  void operator delete(void*);  // expected-note {{non-type aware 'operator delete' declared here}}
+};
+
+struct InclassNew9 {
+  InclassNew9();
+  void *operator new(std::type_identity<InclassNew9>, size_t);
+  // expected-note at -1 {{type aware 'operator new' found in 'InclassNew9'}}
+};
+
+void operator delete(std::type_identity<InclassNew9>, void*);
+// expected-note at -1 {{type aware 'operator delete' found in the global namespace}}
+
+struct BaseClass1 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*) = delete;
+  // expected-note at -1 2 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
+  virtual ~BaseClass1(); // expected-note {{overridden virtual function is here}}
+};
+
+struct SubClass1 : BaseClass1 { 
+  // expected-error at -1 {{deleted function '~SubClass1' cannot override a non-deleted function}}
+  // expected-note at -2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+};
+
+struct BaseClass2 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*) = delete;
+  // expected-note at -1 {{'operator delete<SubClass2>' has been explicitly marked deleted here}}
+  void operator delete(BaseClass2 *, std::destroying_delete_t); 
+  virtual ~BaseClass2();
+};
+
+struct SubClass2 : BaseClass2 {
+  SubClass2(); // Force exception cleanup which should invoke type aware delete
+};
+
+struct BaseClass3 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*);
+  void operator delete(BaseClass3 *, std::destroying_delete_t) = delete;  // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  virtual ~BaseClass3(); // expected-note {{overridden virtual function is here}}
+};
+struct SubClass3 : BaseClass3 {
+  // expected-error at -1 {{deleted function '~SubClass3' cannot override a non-deleted function}}
+  // expected-note at -2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+};
+
+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) = delete;
+  // expected-note at -1 {{candidate function [with T = SubClass4] has been explicitly deleted}}
+  template <Derived<SubClass4> T> void operator delete(std::type_identity<T>, void*) = delete;
+  // expected-note at -1 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  // expected-note at -1 {{candidate function [with T = SubClass4]}}
+  template <typename T> void operator delete(std::type_identity<T>, void*);
+
+  virtual ~BaseClass4(); // expected-note {{overridden virtual function is here}}
+};
+
+struct SubClass4 : BaseClass4 {
+  // expected-error at -1 {{deleted function '~SubClass4' cannot override a non-deleted function}}
+  // expected-note at -2 2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+};
+struct SubClass4_1 : SubClass4 {
+  // expected-note at -1 {{destructor of 'SubClass4_1' is implicitly deleted because base class 'SubClass4' has a deleted destructor}}
+  SubClass4_1();
+};
+struct SubClass4_2 : BaseClass4 {
+};
+
+struct SubClass5;
+struct BaseClass5 {
+  template <Same<SubClass5> T> void *operator new(std::type_identity<T>, size_t);
+  template <Same<SubClass5> T> void operator delete(std::type_identity<T>, void*); // expected-note {{member 'operator delete' declared here}}
+  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void *operator new(std::type_identity<T>, size_t) = delete;
+  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void operator delete(std::type_identity<T>, void*) = delete;
+  // expected-note at -1 {{member 'operator delete' declared here}}
+};
+
+struct SubClass5 : BaseClass5 {
+};
+struct SubClass5_1 : SubClass5 {
+};
+
+
+struct BaseClass6 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  // expected-note at -1 {{type aware 'operator new' found in 'BaseClass6'}}
+  template <typename T> void operator delete(std::type_identity<T>, void*);
+  // expected-note at -1 {{type aware 'operator delete' found in 'BaseClass6'}}
+  BaseClass6();
+  virtual ~BaseClass6();
+};
+
+struct SubClass6_1 : BaseClass6 {
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  // expected-note at -1 {{type aware 'operator new' found in 'SubClass6_1'}}
+  SubClass6_1();
+};
+struct SubClass6_2 : BaseClass6 {
+  template <typename T> void operator delete(std::type_identity<T>, void*);
+  // expected-note at -1 {{type aware 'operator delete' found in 'SubClass6_2'}}
+  SubClass6_2();
+};
+
+
+void test() {
+  
+  // untyped in class declaration wins
+  UntypedInclassNew *O1 = new UntypedInclassNew; // expected-error {{call to deleted function 'operator new'}}
+  delete O1; // expected-error {{attempt to use a deleted function}}
+
+  // untyped in class declaration wins, even though global is aligned and in class is not
+  UntypedInclassNewOveraligned_NoAlignedAlloc *O2 = new UntypedInclassNewOveraligned_NoAlignedAlloc; // expected-error {{call to deleted function 'operator new'}}
+  delete O2; // expected-error {{attempt to use a deleted function}}
+
+  // untyped in class declaration wins
+  UntypedInclassNewOveraligned_AlignedAlloc *O3 = new UntypedInclassNewOveraligned_AlignedAlloc; // expected-error {{call to deleted function 'operator new'}}
+  delete O3; // expected-error {{attempt to use a deleted function}}
+
+  // We resolve the explicitly typed free operator
+  BasicClass *O4 = new BasicClass; // expected-error {{call to deleted function 'operator new'}}
+  delete O4; // expected-error {{attempt to use a deleted function}}
+
+  // We resolve the explicitly typed in class operator
+  InclassNew1 *O5 = new InclassNew1; // expected-error {{call to deleted function 'operator new'}}
+  delete O5; // expected-error {{attempt to use a deleted function}}
+
+  // We resolve the unconstrained in class operators over the constrained free operators
+  InclassNew2 *O6 = new InclassNew2; // expected-error {{call to deleted function 'operator new'}}
+  delete O6; // expected-error {{attempt to use a deleted function}}
+
+  // We prefer the constrained in class operators over the unconstrained variants
+  InclassNew3 *O7 = new InclassNew3; // expected-error {{call to deleted function 'operator new'}}
+  delete O7; // expected-error {{attempt to use a deleted function}}
+
+  // We prefer the aligned but unconstrained operators over the unaligned but constrained variants
+  InclassNew4 *O8 = new InclassNew4; // expected-error {{call to deleted function 'operator new'}}
+  delete O8; // expected-error {{attempt to use a deleted function}}
+
+  // Constructor clean up invokes typed operator if typed new was used
+  InclassNew5 *O9 = new InclassNew5; // expected-error {{attempt to use a deleted function}}
+  delete O9; // expected-error {{attempt to use a deleted function}}
+
+  // Are these reasonable? Should we ensure that declaration of new vs delete have consistent type
+  // semantics? How do we define consistent?
+  // Constructor clean up invokes untyped delete if untyped delete was used
+  InclassNew6 *O10 = new InclassNew6; // expected-error {{attempt to use a deleted function}}
+  // expected-warning at -1 {{mismatching type aware allocation operators for constructor cleanup}}
+  delete O10; //expected-error {{attempt to use a deleted function}}
+
+  // Destroying delete is prefered over typed delete
+  InclassNew7 *O11 = new InclassNew7;
+  delete O11; // expected-error {{attempt to use a deleted function}}
+
+  InclassNew8 *O12 = new InclassNew8;
+  // expected-warning at -1 {{mismatching type aware allocation operators for constructor cleanup}}
+  delete O12;
+
+  InclassNew9 *O13 = new InclassNew9;
+  // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'InclassNew9'}}
+  delete O13;
+
+  // Creating the virtual destructor for an type requires the deleting destructor
+  // for that type
+  SubClass1 *O14 = new SubClass1; // expected-error {{attempt to use a deleted function}}
+  delete O14; // expected-error {{attempt to use a deleted function}}
+
+  SubClass2 *O15 = new SubClass2; // expected-error {{attempt to use a deleted function}}
+  delete O15; 
+
+  // Deletion triggers destroying delete despite type aware delete
+  SubClass3 *O16 = new SubClass3;
+  delete O16; // expected-error {{attempt to use a deleted function}}
+
+  SubClass4 *O17 = new SubClass4; // expected-error {{call to deleted function 'operator new'}}
+  delete O17; // expected-error {{attempt to use a deleted function}}
+
+  SubClass4_1 *O18 = new SubClass4_1;
+  delete O18; // expected-error {{attempt to use a deleted function}}
+
+  SubClass4_2 *O19 = new SubClass4_2;
+  delete O19;
+
+  SubClass5 *O20 = new SubClass5;
+  delete O20;
+
+  SubClass5_1 *O21 = new SubClass5_1; // expected-error {{no matching function for call to 'operator new'}}
+  delete O21; // expected-error {{no suitable member 'operator delete' in 'SubClass5_1'}}
+
+  SubClass6_1 *O22 = new SubClass6_1;
+  // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'SubClass6_1'}}
+  delete O22;
+
+  SubClass6_2 *O23 = new SubClass6_2;
+  // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'BaseClass6'}}
+  delete O23;
+}

>From e721929ee8fae8f186631eb6e23eac08cc375b7c Mon Sep 17 00:00:00 2001
From: Oliver Hunt <github at nerget.com>
Date: Mon, 4 Nov 2024 21:53:51 -0800
Subject: [PATCH 2/6] Update clang/lib/AST/DeclCXX.cpp

Co-authored-by: Erich Keane <ekeane at nvidia.com>
---
 clang/lib/AST/DeclCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index db3d884ec3ec50..b7f6a7449385f5 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2484,7 +2484,7 @@ bool CXXMethodDecl::isUsualDeallocationFunction(
     // Addendum: a template instance is a usual deallocation function if there
     // is a single template parameter, that parameter is a type, only the first
     // parameter is dependent, and that parameter is a specialization of
-    // std::type_identity
+    // std::type_identity.
     if (!IsTypeAware) {
       // Stop early on if the specialization is not explicitly type aware
       return false;

>From fb80c17e88db28785aa4bc73ca3d62526af829d5 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <github at nerget.com>
Date: Mon, 4 Nov 2024 22:02:56 -0800
Subject: [PATCH 3/6] Update clang/lib/AST/Type.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/AST/Type.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 560b42e194a08c..11644de2501d2c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3100,7 +3100,7 @@ bool Type::isStdByteType() const {
 
 TemplateDecl *Type::getSpecializedTemplateDecl() const {
   const auto *DesugaredType = getUnqualifiedDesugaredType();
-  if (auto *Specialization = DesugaredType->getAs<TemplateSpecializationType>())
+  if (const auto *Specialization = DesugaredType->getAs<TemplateSpecializationType>())
     return Specialization->getTemplateName().getAsTemplateDecl();
   if (const auto *Record = DesugaredType->getAsCXXRecordDecl()) {
     if (auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Record))

>From dc4259527d45ef40749218715f50b4779afea9c7 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <github at nerget.com>
Date: Mon, 4 Nov 2024 22:05:20 -0800
Subject: [PATCH 4/6] Update clang/include/clang/Basic/DiagnosticSemaKinds.td

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a21ed3f8da14e..32bf62f1a87cb4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9701,7 +9701,8 @@ def err_no_matching_type_aware_cleanup_deallocator_mismatch : Error<
 def err_type_aware_operator_found : Note<
   "type aware %0 found in %1">;
 def warn_mismatching_type_aware_cleanup_deallocator : Warning<
-  "mismatching type aware allocation operators for constructor cleanup">, InGroup<TypeAwareAllocatorMismatch>;
+  "mismatched type aware allocation operators for constructor cleanup">,
+  InGroup<TypeAwareAllocatorMismatch>;
 def note_type_aware_operator_declared : Note<
   "%select{|non-}0type aware %1 declared here">;
 def note_implicit_delete_this_in_destructor_here : Note<

>From 728aa09320e177c332f4fb73923b17fb7c312eb2 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Fri, 8 Nov 2024 13:30:23 -0800
Subject: [PATCH 5/6] Addressing review comments

---
 clang/include/clang/AST/Decl.h                |   2 +-
 clang/include/clang/AST/Type.h                |   2 +-
 clang/include/clang/Sema/Sema.h               |  12 +-
 clang/lib/AST/Decl.cpp                        |   8 +-
 clang/lib/AST/DeclCXX.cpp                     |  10 +-
 clang/lib/AST/Type.cpp                        |   4 +-
 clang/lib/CodeGen/CGExprCXX.cpp               |   2 +-
 clang/lib/Sema/SemaCoroutine.cpp              |   8 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  66 ++--
 clang/lib/Sema/SemaExprCXX.cpp                |  64 ++--
 .../test/SemaCXX/type-aware-new-constexpr.cpp |  38 ++-
 .../SemaCXX/type-aware-new-delete-arrays.cpp  |  30 +-
 ...are-new-delete-basic-free-declarations.cpp |  84 ++---
 ...new-delete-basic-in-class-declarations.cpp |  44 +--
 ...type-aware-new-delete-basic-resolution.cpp | 310 +++++++++++-------
 15 files changed, 378 insertions(+), 306 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 5235ace380bd9e..5c7a4780e8078f 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2530,7 +2530,7 @@ class FunctionDecl : public DeclaratorDecl,
   /// Determine whether this is a destroying operator delete.
   bool isDestroyingOperatorDelete() const;
 
-  bool IsTypeAwareOperatorNewOrDelete() const;
+  bool isTypeAwareOperatorNewOrDelete() const;
 
   /// Compute the language linkage.
   LanguageLinkage getLanguageLinkage() const;
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index b7ee0f2f5cf046..132610c441a6eb 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2703,7 +2703,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     return static_cast<TypeDependence>(TypeBits.Dependence);
   }
 
-  TemplateDecl *getSpecializedTemplateDecl() const;
+  const TemplateDecl *getSpecializedTemplateDecl() const;
 
   /// Whether this type is an error type.
   bool containsErrors() const {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 90bc45a5567faa..3baca45ae1c39d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4756,13 +4756,13 @@ class Sema final : public SemaBase {
   CXXRecordDecl *getStdBadAlloc() const;
   EnumDecl *getStdAlignValT() const;
   ClassTemplateDecl *getStdTypeIdentity() const;
-  std::optional<QualType> InstantiateSpecializedTypeIdentity(QualType Subject);
-  bool IsTypeIdentitySpecialization(QualType Type) const;
-  bool IsTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const;
-  bool IsTypeAwareOperatorNewOrDelete(const FunctionTemplateDecl *FnDecl) const;
-  bool IsTypeAwareOperatorNewOrDelete(const NamedDecl *FnDecl) const;
+  std::optional<QualType> instantiateSpecializedTypeIdentity(QualType Subject);
+  bool isTypeIdentitySpecialization(QualType Type) const;
+  bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const;
+  bool isTypeAwareOperatorNewOrDelete(const FunctionTemplateDecl *FnDecl) const;
+  bool isTypeAwareOperatorNewOrDelete(const NamedDecl *FnDecl) const;
   std::optional<FunctionDecl *>
-  InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnDecl,
+  instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnDecl,
                                   QualType AllocType);
 
   ValueDecl *tryLookupUnambiguousFieldDecl(RecordDecl *ClassDecl,
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index c0d0336e1db4a3..9434d5647e4ea7 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3360,9 +3360,7 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
   const auto *proto = getType()->castAs<FunctionProtoType>();
   if (proto->getNumParams() < 2)
     return false;
-  bool IsTypeAwareAllocator =
-      proto->getParamType(0)->isTypeIdentitySpecialization();
-  if (IsTypeAwareAllocator)
+  if (proto->getParamType(0)->isTypeIdentitySpecialization())
     return false;
   if (proto->getNumParams() != 2 || proto->isVariadic())
     return false;
@@ -3492,7 +3490,7 @@ bool FunctionDecl::isDestroyingOperatorDelete() const {
   if (!isa<CXXMethodDecl>(this) || getOverloadedOperator() != OO_Delete)
     return false;
 
-  auto NumParams = getNumParams();
+  unsigned NumParams = getNumParams();
   unsigned DestroyingDeleteTagParam = 1;
   bool IsTypeAware = false;
   if (NumParams > 0)
@@ -3510,7 +3508,7 @@ bool FunctionDecl::isDestroyingOperatorDelete() const {
          RD->getIdentifier()->isStr("destroying_delete_t");
 }
 
-bool FunctionDecl::IsTypeAwareOperatorNewOrDelete() const {
+bool FunctionDecl::isTypeAwareOperatorNewOrDelete() const {
   if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
     return false;
   if (getDeclName().getCXXOverloadedOperator() != OO_New &&
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index b7f6a7449385f5..dea5e36509aa73 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2474,13 +2474,13 @@ bool CXXMethodDecl::isUsualDeallocationFunction(
       getOverloadedOperator() != OO_Array_Delete)
     return false;
 
-  auto NumParams = getNumParams();
-  bool IsTypeAware = IsTypeAwareOperatorNewOrDelete();
+  unsigned NumParams = getNumParams();
+  bool IsTypeAware = isTypeAwareOperatorNewOrDelete();
 
   // C++ [basic.stc.dynamic.deallocation]p2:
   //   A template instance is never a usual deallocation function,
   //   regardless of its signature.
-  if (auto *PrimaryTemplate = getPrimaryTemplate()) {
+  if (FunctionTemplateDecl *PrimaryTemplate = getPrimaryTemplate()) {
     // Addendum: a template instance is a usual deallocation function if there
     // is a single template parameter, that parameter is a type, only the first
     // parameter is dependent, and that parameter is a specialization of
@@ -2490,8 +2490,8 @@ bool CXXMethodDecl::isUsualDeallocationFunction(
       return false;
     }
 
-    auto *SpecializedDecl = PrimaryTemplate->getTemplatedDecl();
-    if (!SpecializedDecl->IsTypeAwareOperatorNewOrDelete()) {
+    FunctionDecl *SpecializedDecl = PrimaryTemplate->getTemplatedDecl();
+    if (!SpecializedDecl->isTypeAwareOperatorNewOrDelete()) {
       // The underlying template decl must also be explicitly type aware
       // e.g. template <typename T> void operator delete(T, ...)
       // specialized on T=type_identity<X> is not usual
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 11644de2501d2c..4658dc1ae749b7 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3098,8 +3098,8 @@ bool Type::isStdByteType() const {
   return false;
 }
 
-TemplateDecl *Type::getSpecializedTemplateDecl() const {
-  const auto *DesugaredType = getUnqualifiedDesugaredType();
+const TemplateDecl *Type::getSpecializedTemplateDecl() const {
+  const Type *DesugaredType = getUnqualifiedDesugaredType();
   if (const auto *Specialization = DesugaredType->getAs<TemplateSpecializationType>())
     return Specialization->getTemplateName().getAsTemplateDecl();
   if (const auto *Record = DesugaredType->getAsCXXRecordDecl()) {
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index fa4695e0e4f382..dae8dc12c1e0ea 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1393,7 +1393,7 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
   const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>();
   auto AI = FPT->param_type_begin(), AE = FPT->param_type_end();
 
-  if (FD->IsTypeAwareOperatorNewOrDelete()) {
+  if (FD->isTypeAwareOperatorNewOrDelete()) {
     // Assume Sema has ensured a non-pointer first parameter is
     // a type identity
     Params.TypedAwareDelete = true;
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 595bdd27eea314..cd9581dc6b85e5 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1599,13 +1599,13 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
 
   SmallVector<Expr *, 3> NewArgs;
   if (IAP.PassTypeIdentity) {
-    auto SpecializedTypeIdentity =
-        S.InstantiateSpecializedTypeIdentity(PromiseType);
+    std::optional<QualType> SpecializedTypeIdentity =
+        S.instantiateSpecializedTypeIdentity(PromiseType);
     if (!SpecializedTypeIdentity)
       return false;
-    auto *SpecializedTypeInfo =
+    TypeSourceInfo *SpecializedTypeInfo =
         S.Context.getTrivialTypeSourceInfo(*SpecializedTypeIdentity, Loc);
-    auto TypeIdentity =
+    ExprResult TypeIdentity =
         S.BuildCXXTypeConstructExpr(SpecializedTypeInfo, Loc, {}, Loc, false);
     if (TypeIdentity.isInvalid())
       return false;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8c48ecf92eda48..045c79da32c07f 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9711,7 +9711,7 @@ 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;
-    auto DeallocType = Context.getRecordType(RD);
+    QualType DeallocType = Context.getRecordType(RD);
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
     ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
@@ -10872,7 +10872,7 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
       // first parameter, perform that conversion now.
       if (OperatorDelete->isDestroyingOperatorDelete()) {
         unsigned PointerParam = 0;
-        if (IsTypeAwareOperatorNewOrDelete(OperatorDelete)) {
+        if (isTypeAwareOperatorNewOrDelete(OperatorDelete)) {
           ++PointerParam;
         }
         QualType ParamType =
@@ -16123,47 +16123,47 @@ bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
   return Invalid;
 }
 
-bool Sema::IsTypeIdentitySpecialization(QualType Type) const {
-  auto *TypeIdentity = getStdTypeIdentity();
+bool Sema::isTypeIdentitySpecialization(QualType Type) const {
+  ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
   if (!TypeIdentity)
     return false;
-  auto *SpecializedDecl = Type->getSpecializedTemplateDecl();
+  const TemplateDecl *SpecializedDecl = Type->getSpecializedTemplateDecl();
   return TypeIdentity == SpecializedDecl;
 }
 
-bool Sema::IsTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const {
+bool Sema::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const {
   // Type aware operators
   if (FnDecl->getNumParams() < 2)
     return false;
-  const auto *ParamDecl = FnDecl->getParamDecl(0);
-  return IsTypeIdentitySpecialization(ParamDecl->getType());
+  const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(0);
+  return isTypeIdentitySpecialization(ParamDecl->getType());
 }
 
-bool Sema::IsTypeAwareOperatorNewOrDelete(
+bool Sema::isTypeAwareOperatorNewOrDelete(
     const FunctionTemplateDecl *FTD) const {
-  return IsTypeAwareOperatorNewOrDelete(FTD->getTemplatedDecl());
+  return isTypeAwareOperatorNewOrDelete(FTD->getTemplatedDecl());
 }
 
-bool Sema::IsTypeAwareOperatorNewOrDelete(const NamedDecl *ND) const {
+bool Sema::isTypeAwareOperatorNewOrDelete(const NamedDecl *ND) const {
   if (auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
-    return IsTypeAwareOperatorNewOrDelete(FTD->getTemplatedDecl());
+    return isTypeAwareOperatorNewOrDelete(FTD->getTemplatedDecl());
   if (auto *FnDecl = dyn_cast<FunctionDecl>(ND))
-    return IsTypeAwareOperatorNewOrDelete(FnDecl);
+    return isTypeAwareOperatorNewOrDelete(FnDecl);
   return false;
 }
 
 std::optional<FunctionDecl *>
-Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
+Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
                                       QualType DeallocType) {
   if (!AllowTypeAwareAllocators())
     return std::nullopt;
 
-  auto TemplateParameters = FnTemplateDecl->getTemplateParameters();
+  TemplateParameterList *TemplateParameters = FnTemplateDecl->getTemplateParameters();
   if (TemplateParameters->hasParameterPack())
     return std::nullopt;
 
-  auto FnDecl = FnTemplateDecl->getTemplatedDecl();
-  if (!IsTypeAwareOperatorNewOrDelete(FnDecl))
+  FunctionDecl *FnDecl = FnTemplateDecl->getTemplatedDecl();
+  if (!isTypeAwareOperatorNewOrDelete(FnDecl))
     return std::nullopt;
 
   if (FnDecl->isVariadic())
@@ -16176,13 +16176,13 @@ Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
   for (size_t Idx = 1; Idx < NumParams; ++Idx) {
     // A type aware allocation is only usual if the only dependent parameter is
     // the first parameter
-    const auto *ParamDecl = FnDecl->getParamDecl(Idx);
+    const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(Idx);
     if (ParamDecl->getType()->isDependentType())
       return std::nullopt;
   }
 
-  auto SpecializedTypeIdentity =
-      InstantiateSpecializedTypeIdentity(DeallocType);
+  std::optional<QualType> SpecializedTypeIdentity =
+      instantiateSpecializedTypeIdentity(DeallocType);
   if (!SpecializedTypeIdentity)
     return std::nullopt;
   SmallVector<QualType, 4> ArgTypes;
@@ -16191,13 +16191,13 @@ Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
   ArgTypes.push_back(FnDecl->getParamDecl(1)->getType());
   unsigned UsualParamsIdx = 2;
   if (UsualParamsIdx < NumParams && FnDecl->isDestroyingOperatorDelete()) {
-    auto Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
+    QualType Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
     ArgTypes.push_back(Type);
     ++UsualParamsIdx;
   }
 
   if (UsualParamsIdx < NumParams) {
-    auto Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
+    QualType Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
     if (Context.hasSameUnqualifiedType(Type, Context.getSizeType())) {
       ArgTypes.push_back(Type);
       ++UsualParamsIdx;
@@ -16205,7 +16205,7 @@ Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
   }
 
   if (UsualParamsIdx < NumParams) {
-    auto Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
+    QualType Type = FnDecl->getParamDecl(UsualParamsIdx)->getType();
     if (Type->isAlignValT()) {
       ArgTypes.push_back(Type);
       ++UsualParamsIdx;
@@ -16216,7 +16216,7 @@ Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
     return std::nullopt;
 
   FunctionProtoType::ExtProtoInfo EPI;
-  auto ExpectedFunctionType =
+  QualType ExpectedFunctionType =
       Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
   SourceLocation Loc;
   sema::TemplateDeductionInfo Info(Loc);
@@ -16228,9 +16228,9 @@ Sema::InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
 }
 
 std::optional<QualType>
-Sema::InstantiateSpecializedTypeIdentity(QualType Subject) {
+Sema::instantiateSpecializedTypeIdentity(QualType Subject) {
   assert(AllowTypeAwareAllocators());
-  auto *TypeIdentity = getStdTypeIdentity();
+  ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
   if (!TypeIdentity) {
     return std::nullopt;
   }
@@ -16238,7 +16238,7 @@ Sema::InstantiateSpecializedTypeIdentity(QualType Subject) {
   TemplateArgumentListInfo Arguments;
   Arguments.addArgument(getTrivialTemplateArgumentLoc(
       TemplateArgument(Subject), QualType(), SourceLocation()));
-  auto Result = CheckTemplateIdType(TN, SourceLocation(), Arguments);
+  QualType Result = CheckTemplateIdType(TN, SourceLocation(), Arguments);
   if (Result.isNull())
     return std::nullopt;
   return Result;
@@ -16288,8 +16288,8 @@ static inline bool CheckOperatorNewDeleteTypes(
   };
   auto *FnType = FnDecl->getType()->castAs<FunctionType>();
   QualType CanResultType = NormalizeType(FnType->getReturnType());
-  auto CanExpectedResultType = NormalizeType(ExpectedResultType);
-  auto CanExpectedFirstParamType = NormalizeType(ExpectedFirstParamType);
+  QualType CanExpectedResultType = NormalizeType(ExpectedResultType);
+  QualType CanExpectedFirstParamType = NormalizeType(ExpectedFirstParamType);
 
   // Check that the result type is what we expect.
   if (CanResultType != CanExpectedResultType) {
@@ -16310,7 +16310,7 @@ static inline bool CheckOperatorNewDeleteTypes(
         << FnDecl->getDeclName();
 
   unsigned FirstNonTypeParam = 0;
-  if (FnDecl->IsTypeAwareOperatorNewOrDelete()) {
+  if (FnDecl->isTypeAwareOperatorNewOrDelete()) {
     if (!SemaRef.getLangOpts().TypeAwareAllocators) {
       return SemaRef.Diag(FnDecl->getLocation(),
                           diag::err_unsupported_type_aware_allocator);
@@ -16367,7 +16367,7 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
   // C++ [basic.stc.dynamic.allocation]p1:
   //  The first parameter shall not have an associated default argument.
   for (unsigned Idx = 0; Idx < MinimumNonDefaultArgs; ++Idx) {
-    auto *ParamDecl = FnDecl->getParamDecl(Idx);
+    const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(Idx);
     if (ParamDecl->hasDefaultArg()) {
       return SemaRef.Diag(FnDecl->getLocation(),
                           diag::err_operator_new_default_arg)
@@ -16417,7 +16417,7 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
                    diag::err_destroying_operator_delete_not_usual);
       return true;
     }
-    if (MD->IsTypeAwareOperatorNewOrDelete() &&
+    if (MD->isTypeAwareOperatorNewOrDelete() &&
         !SemaRef.getLangOpts().TypeAwareDestroyingDelete) {
       SemaRef.Diag(MD->getLocation(),
                    diag::err_type_aware_destroying_operator_delete);
@@ -16425,7 +16425,7 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
     }
   }
   for (unsigned Idx = 0; Idx < MinimumNonDefaultArgs; ++Idx) {
-    auto *ParamDecl = FnDecl->getParamDecl(Idx);
+    const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(Idx);
     if (ParamDecl->hasDefaultArg()) {
       return SemaRef.Diag(FnDecl->getLocation(),
                           diag::err_operator_new_default_arg)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index ad32debe0d24eb..435187ada01513 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1755,7 +1755,7 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
 
   unsigned UsualParams = 1;
   if (S.AllowTypeAwareAllocators() && UsualParams < FD->getNumParams() &&
-      S.IsTypeAwareOperatorNewOrDelete(FD))
+      S.isTypeAwareOperatorNewOrDelete(FD))
     ++UsualParams;
 
   if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() &&
@@ -1786,18 +1786,18 @@ namespace {
         auto *FTD = dyn_cast<FunctionTemplateDecl>(Found->getUnderlyingDecl());
         if (!FTD)
           return;
-        auto InstantiatedDecl =
-            S.InstantiateTypeAwareUsualDelete(FTD, AllocType);
+        std::optional<FunctionDecl *> InstantiatedDecl =
+            S.instantiateTypeAwareUsualDelete(FTD, AllocType);
         if (!InstantiatedDecl)
           return;
         FD = *InstantiatedDecl;
       }
       unsigned NumBaseParams = 1;
-      if (S.IsTypeAwareOperatorNewOrDelete(FD) &&
+      if (S.isTypeAwareOperatorNewOrDelete(FD) &&
           S.AllowTypeAwareAllocators()) {
-        auto TypeIdentityTag = FD->getParamDecl(0)->getType();
-        auto ExpectedTypeIdentityTag =
-            S.InstantiateSpecializedTypeIdentity(AllocType);
+        QualType TypeIdentityTag = FD->getParamDecl(0)->getType();
+        std::optional<QualType> ExpectedTypeIdentityTag =
+            S.instantiateSpecializedTypeIdentity(AllocType);
         if (!ExpectedTypeIdentityTag) {
           FD = nullptr;
           return;
@@ -1862,8 +1862,8 @@ namespace {
       if (HasTypeIdentity) {
         // Type aware allocation involves templates so we need to choose
         // the best type
-        auto *PrimaryTemplate = FD->getPrimaryTemplate();
-        auto *OtherPrimaryTemplate = Other.FD->getPrimaryTemplate();
+        FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
+        FunctionTemplateDecl *OtherPrimaryTemplate = Other.FD->getPrimaryTemplate();
         if ((!PrimaryTemplate) != (!OtherPrimaryTemplate))
           return OtherPrimaryTemplate ? 1 : -1;
 
@@ -1913,8 +1913,8 @@ static bool CheckDeleteOperator(Sema &S, SourceLocation StartLoc,
                                 SourceRange Range, bool Diagnose,
                                 CXXRecordDecl *NamingClass, DeclAccessPair Decl,
                                 FunctionDecl *Operator) {
-  if (S.IsTypeAwareOperatorNewOrDelete(Operator)) {
-    auto SelectedTypeIdentityParameter = Operator->getParamDecl(0)->getType();
+  if (S.isTypeAwareOperatorNewOrDelete(Operator)) {
+    QualType SelectedTypeIdentityParameter = Operator->getParamDecl(0)->getType();
     if (S.RequireCompleteType(StartLoc, SelectedTypeIdentityParameter,
                               diag::err_incomplete_type))
       return true;
@@ -1954,7 +1954,7 @@ static UsualDeallocFnInfo resolveDeallocationOverload(
         BestFns->push_back(Info);
       continue;
     }
-    auto ComparisonResult = Best.Compare(S, Info, IDP);
+    int ComparisonResult = Best.Compare(S, Info, IDP);
     if (ComparisonResult > 0)
       continue;
 
@@ -2454,7 +2454,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     // alignment; we've already filled it in.
     unsigned NumImplicitArgs = 1;
     if (IAP.PassTypeIdentity) {
-      assert(OperatorNew->IsTypeAwareOperatorNewOrDelete());
+      assert(OperatorNew->isTypeAwareOperatorNewOrDelete());
       NumImplicitArgs++;
     }
     if (IAP.PassAlignment)
@@ -2680,7 +2680,7 @@ static bool resolveAllocationOverloadInterior(
     // Even member operator new/delete are implicitly treated as
     // static, so don't use AddMemberCandidate.
     NamedDecl *D = (*Alloc)->getUnderlyingDecl();
-    if (S.IsTypeAwareOperatorNewOrDelete(D) == (Mode != ResolveMode::Typed))
+    if (S.isTypeAwareOperatorNewOrDelete(D) == (Mode != ResolveMode::Typed))
       continue;
 
     if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
@@ -2835,8 +2835,8 @@ static void LookupGlobalDeallocationFunctions(Sema &S, SourceLocation Loc,
     bool RemoveTypedDecl = Mode == DeallocLookupMode::Untyped;
     LookupResult::Filter Filter = FoundDelete.makeFilter();
     while (Filter.hasNext()) {
-      auto Decl = Filter.next()->getUnderlyingDecl();
-      bool DeclIsTypeAware = S.IsTypeAwareOperatorNewOrDelete(Decl);
+      NamedDecl *Decl = Filter.next()->getUnderlyingDecl();
+      bool DeclIsTypeAware = S.isTypeAwareOperatorNewOrDelete(Decl);
       if (DeclIsTypeAware && RemoveTypedDecl)
         Filter.erase();
     }
@@ -2883,8 +2883,8 @@ bool Sema::FindAllocationFunctions(
   // --- Choosing an allocation function ---
   // C++ 5.3.4p8 - 14 & 18
   // 1) If looking in AFS_Global scope for allocation functions, only look in
-  //    the global scope or ADL associated namespaces. Else, if AFS_Class, only
-  //    look in the scope of the allocated class. If AFS_Both, look in both.
+  //    the global scope. Else, if AFS_Class, only look in the scope of the
+  //    allocated class. If AFS_Both, look in both.
   // 2) If an array size is given, look for operator new[], else look for
   //   operator new.
   // 3) The first argument is always size_t. Append the arguments from the
@@ -2914,8 +2914,8 @@ bool Sema::FindAllocationFunctions(
   // expr on the stack
   QualType TypeIdentity = Context.getSizeType();
   if (IAP.PassTypeIdentity) {
-    if (auto SpecializedTypeIdentity =
-            InstantiateSpecializedTypeIdentity(AllocElemType)) {
+    if (std::optional<QualType> SpecializedTypeIdentity =
+            instantiateSpecializedTypeIdentity(AllocElemType)) {
       TypeIdentity = *SpecializedTypeIdentity;
     } else {
       IAP.PassTypeIdentity = false;
@@ -3043,7 +3043,7 @@ bool Sema::FindAllocationFunctions(
       return true;
 
     DeclareGlobalNewDelete();
-    auto LookupMode = OriginalTypeAwareState
+    DeallocLookupMode LookupMode = OriginalTypeAwareState
                           ? DeallocLookupMode::OptionallyTyped
                           : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
@@ -3106,7 +3106,7 @@ bool Sema::FindAllocationFunctions(
     }
 
     for (LookupResult::iterator D = FoundDelete.begin(),
-                                DEnd = FoundDelete.end();
+                             DEnd = FoundDelete.end();
          D != DEnd; ++D) {
       FunctionDecl *Fn = nullptr;
       if (FunctionTemplateDecl *FnTmpl =
@@ -3161,14 +3161,14 @@ bool Sema::FindAllocationFunctions(
   //   deallocation function will be called.
   if (Matches.size() == 1) {
     OperatorDelete = Matches[0].second;
-    if (IsTypeAwareOperatorNewOrDelete(OperatorDelete) !=
+    if (isTypeAwareOperatorNewOrDelete(OperatorDelete) !=
         IAP.PassTypeIdentity) {
       Diag(StartLoc, diag::warn_mismatching_type_aware_cleanup_deallocator);
-      int NewDiagIndex = IsTypeAwareOperatorNewOrDelete(OperatorNew) ? 0 : 1;
+      int NewDiagIndex = isTypeAwareOperatorNewOrDelete(OperatorNew) ? 0 : 1;
       int DeleteDiagIndex =
-          IsTypeAwareOperatorNewOrDelete(OperatorDelete) ? 0 : 1;
+          isTypeAwareOperatorNewOrDelete(OperatorDelete) ? 0 : 1;
       Diag(OperatorNew->getLocation(), diag::note_type_aware_operator_declared)
-          << NewDiagIndex << OperatorNew->getDeclName();
+          << NewDiagIndex << OperatorNew;
       Diag(OperatorDelete->getLocation(),
            diag::note_type_aware_operator_declared)
           << DeleteDiagIndex << OperatorDelete->getDeclName();
@@ -3177,10 +3177,10 @@ bool Sema::FindAllocationFunctions(
         OperatorDelete->getDeclContext() != OperatorNew->getDeclContext()) {
       Diag(StartLoc,
            diag::err_no_matching_type_aware_cleanup_deallocator_mismatch)
-          << OperatorNew->getDeclName() << DeleteName
+          << OperatorNew << DeleteName
           << OperatorNew->getDeclContext();
       Diag(OperatorNew->getLocation(), diag::err_type_aware_operator_found)
-          << OperatorNew->getDeclName() << OperatorNew->getDeclContext();
+          << OperatorNew << OperatorNew->getDeclContext();
       Diag(OperatorDelete->getLocation(), diag::err_type_aware_operator_found)
           << OperatorDelete->getDeclName() << OperatorDelete->getDeclContext();
     }
@@ -3495,7 +3495,7 @@ FunctionDecl *Sema::FindUsualDeallocationFunction(
   DeclareGlobalNewDelete();
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
-  auto LookupMode = AllowTypeAwareAllocators()
+  DeallocLookupMode LookupMode = AllowTypeAwareAllocators()
                         ? DeallocLookupMode::OptionallyTyped
                         : DeallocLookupMode::Untyped;
   LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
@@ -3524,7 +3524,7 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
   DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete);
 
   FunctionDecl *OperatorDelete = nullptr;
-  auto DeallocType = Context.getRecordType(RD);
+  QualType DeallocType = Context.getRecordType(RD);
   ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
                                             AllowTypeAwareAllocators(),
                                         .PassAlignment = false,
@@ -3538,7 +3538,7 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
 
   // If there's no class-specific operator delete, look up the global
   // non-array delete.
-  auto RecordType = Context.getRecordType(RD);
+  QualType RecordType = Context.getRecordType(RD);
   IDP.PassAlignment = hasNewExtendedAlignment(*this, RecordType);
   IDP.PassSize = true;
   return FindUsualDeallocationFunction(RecordType, Loc, IDP, Name);
@@ -4084,7 +4084,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
     // delete that we are going to call (non-virtually); converting to void*
     // is trivial and left to AST consumers to handle.
     unsigned PointeeIndex = 0;
-    if (IsTypeAwareOperatorNewOrDelete(OperatorDelete))
+    if (isTypeAwareOperatorNewOrDelete(OperatorDelete))
       PointeeIndex = 1;
     QualType ParamType = OperatorDelete->getParamDecl(PointeeIndex)->getType();
     if (!IsVirtualDelete && !ParamType->getPointeeType()->isVoidType()) {
diff --git a/clang/test/SemaCXX/type-aware-new-constexpr.cpp b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
index 962d03c6839adb..a8f3fa4d0d0169 100644
--- a/clang/test/SemaCXX/type-aware-new-constexpr.cpp
+++ b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
@@ -13,9 +13,8 @@ struct S1 {
   const int i;
 };
 
-void *operator new(std::type_identity<S1>, size_t sz);
-// expected-note at -1 {{candidate function not viable: no known conversion from 'type_identity<S2>' to 'type_identity<S1>' for 1st argument}}
-void operator delete(std::type_identity<S1>, void* ptr);
+void *operator new(std::type_identity<S1>, size_t sz); // #1
+void operator delete(std::type_identity<S1>, void* ptr); // #2
 
 constexpr int ensure_consteval_skips_typed_allocators() {
   // Verify we dont resolve typed allocators in const contexts
@@ -30,16 +29,19 @@ struct S2 {
   const int i;
 };
 
-void *operator new(std::type_identity<S2>, size_t sz) = delete;
-// expected-note at -1 {{candidate function has been explicitly deleted}}
-void operator delete(std::type_identity<S2>, void* ptr) = delete;
-// expected-note at -1 {{'operator delete' has been explicitly marked deleted here}}
+void *operator new(std::type_identity<S2>, size_t sz) = delete; // #3
+void operator delete(std::type_identity<S2>, void* ptr) = 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 {{call to deleted function 'operator new'}}
+  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 {{attempt to use a deleted function}}
+  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;
 };
 
@@ -47,27 +49,29 @@ constexpr int ensure_constexpr_retains_types_at_runtime() {
 struct S3 {
   constexpr explicit S3() : i(5) {  }
   const int i;
-  template <typename T> void* operator new(std::type_identity<T>, size_t sz) = delete;
-  // expected-note at -1 {{candidate function [with T = S3] has been explicitly deleted}}
-  template <typename T> void operator delete(std::type_identity<T>, void *) = delete;
-  // expected-note at -1 {{'operator delete<S3>' has been explicitly marked deleted here}}
+  template <typename T> void* operator new(std::type_identity<T>, size_t sz) = delete; // #5
+  template <typename T> void operator delete(std::type_identity<T>, void *) = delete; // #6
 };
 
-template <typename T> void* operator new(std::type_identity<T>, size_t sz) = delete;
-template <typename T> void operator delete(std::type_identity<T>, void *) = delete;
+template <typename T> void* operator new(std::type_identity<T>, size_t sz) = delete; // #7
+template <typename T> void operator delete(std::type_identity<T>, void *) = delete; // #8
 
 constexpr int constexpr_vs_inclass_operators() {
   S3 *s;
   if consteval {
     s = ::new S3();
   } else {
-    s = new S3(); // expected-error {{call to deleted function 'operator new'}}
+    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;
   } else {
-    delete s; // expected-error {{attempt to use a deleted function}}
+    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;
 };
diff --git a/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp b/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
index d46bc7bd845c60..2ead0fb2110404 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions -Wall -Wpedantic
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions -Wall -Wpedantic
 
 namespace std {
   template <class T> struct type_identity {};
@@ -9,10 +9,8 @@ namespace std {
 using size_t = __SIZE_TYPE__;
 
 struct BasicTypeAwareArrayAllocator {
-   template <typename T> void *operator new[](std::type_identity<T>, size_t) = delete;
-   // expected-note at -1 {{candidate function [with T = BasicTypeAwareArrayAllocator] has been explicitly deleted}}
-   template <typename T> void  operator delete[](std::type_identity<T>, void*) = delete;
-   // expected-note at -1 {{'operator delete[]<BasicTypeAwareArrayAllocator>' has been explicitly marked deleted here}}
+   template <typename T> void *operator new[](std::type_identity<T>, size_t) = delete; // #1
+   template <typename T> void  operator delete[](std::type_identity<T>, void*) = delete; // #2
 };
 void *operator new[](std::type_identity<BasicTypeAwareArrayAllocator>, size_t);
 void  operator delete[](std::type_identity<BasicTypeAwareArrayAllocator>, void*);
@@ -34,16 +32,24 @@ void  operator delete[](std::type_identity<WorkingTypeAwareAllocator>, void*) =
 
 
 void test() {
-  BasicTypeAwareArrayAllocator *A0 = new BasicTypeAwareArrayAllocator[10]; // expected-error {{call to deleted function 'operator new[]'}}
-  delete [] A0; // expected-error {{attempt to use a deleted function}}
+  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]; // ex
+  BasicTypeAwareNonArrayAllocator *A1 = new BasicTypeAwareNonArrayAllocator[10];
   delete [] A1;
 
-  WorkingTypeAwareAllocator *A2 = new WorkingTypeAwareAllocator[10]; // expected-note {{allocated with 'new[]' here}}
-  delete A2; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+  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 {{allocated with 'new' here}}
-  delete [] A3; // expected-warning {{'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
index e0dc0612a16def..b842ffcead91b5 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp
@@ -13,70 +13,70 @@ struct TestType {};
 template <typename T> struct TemplateTestType {};
 
 // Valid free declarations
-void *operator new(std::type_identity<int>, size_t);
-void *operator new(std::type_identity<int>, size_t, std::align_val_t);
-void *operator new(std::type_identity<int>, size_t, TestType&);
-template <typename T> void *operator new(std::type_identity<T>, size_t);
-template <typename T> void *operator new(std::type_identity<T>, size_t, TestType&);
-template <typename T> void *operator new(std::type_identity<TemplateTestType<T>>, size_t, TestType&);
-template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, TemplateTestType<U>&);
-template <template <typename> class T> void *operator new(std::type_identity<T<int>>, size_t); 
+void *operator new(std::type_identity<int>, size_t); // #1
+void *operator new(std::type_identity<int>, size_t, std::align_val_t); // #2
+void *operator new(std::type_identity<int>, size_t, TestType&); // #3
+template <typename T> void *operator new(std::type_identity<T>, size_t); // #4
+template <typename T> void *operator new(std::type_identity<T>, size_t, TestType&); // #5
+template <typename T> void *operator new(std::type_identity<TemplateTestType<T>>, size_t, TestType&); // #6
+template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, TemplateTestType<U>&); // #7
+template <template <typename> class T> void *operator new(std::type_identity<T<int>>, size_t); // #8
 #if defined(NO_TAA)
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
-//expected-error at -9 {{type aware allocation operators are disabled}}
+//expected-error@#1 {{type aware allocation operators are disabled}}
+//expected-error@#2 {{type aware allocation operators are disabled}}
+//expected-error@#3 {{type aware allocation operators are disabled}}
+//expected-error@#4 {{type aware allocation operators are disabled}}
+//expected-error@#5 {{type aware allocation operators are disabled}}
+//expected-error@#6 {{type aware allocation operators are disabled}}
+//expected-error@#7 {{type aware allocation operators are disabled}}
+//expected-error@#8 {{type aware allocation operators are disabled}}
 #endif
 
-void operator delete(std::type_identity<int>, void *);
-void operator delete(std::type_identity<int>, void *, std::align_val_t);
-void operator delete(std::type_identity<int>, void *, size_t);
-void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t);
+void operator delete(std::type_identity<int>, void *); // #9
+void operator delete(std::type_identity<int>, void *, std::align_val_t); // #10
+void operator delete(std::type_identity<int>, void *, size_t); // #11
+void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #12
 #if defined(NO_TAA)
-//expected-error at -5 {{type aware allocation operators are disabled}}
-//expected-error at -5 {{type aware allocation operators are disabled}}
-//expected-error at -5 {{type aware allocation operators are disabled}}
-//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error@#9 {{type aware allocation operators are disabled}}
+//expected-error@#10 {{type aware allocation operators are disabled}}
+//expected-error@#11 {{type aware allocation operators are disabled}}
+//expected-error@#12 {{type aware allocation operators are disabled}}
 #endif
 
-template <typename T> void operator delete(std::type_identity<T>, void *);
-template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t);
-template <typename T> void operator delete(std::type_identity<T>, void *, size_t);
-template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
+template <typename T> void operator delete(std::type_identity<T>, void *); // #13
+template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t); // #14
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t); // #15
+template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #16
 #if defined(NO_TAA)
-//expected-error at -5 {{type aware allocation operators are disabled}}
-//expected-error at -5 {{type aware allocation operators are disabled}}
-//expected-error at -5 {{type aware allocation operators are disabled}}
-//expected-error at -5 {{type aware allocation operators are disabled}}
+//expected-error@#13 {{type aware allocation operators are disabled}}
+//expected-error@#14 {{type aware allocation operators are disabled}}
+//expected-error@#15 {{type aware allocation operators are disabled}}
+//expected-error@#16 {{type aware allocation operators are disabled}}
 #endif
 
-template <typename T> void operator delete(std::type_identity<TemplateTestType<T>>, void *);
-template <template <typename> class T> void operator delete(std::type_identity<T<int>>, void *);
+template <typename T> void operator delete(std::type_identity<TemplateTestType<T>>, void *); // #17
+template <template <typename> class T> void operator delete(std::type_identity<T<int>>, void *); // #18
 #if defined(NO_TAA)
-//expected-error at -3 {{type aware allocation operators are disabled}}
-//expected-error at -3 {{type aware allocation operators are disabled}}
+//expected-error@#17 {{type aware allocation operators are disabled}}
+//expected-error@#18 {{type aware allocation operators are disabled}}
 #endif
 
 typedef std::type_identity<float> TypeIdentityAlias1;
-void *operator new(TypeIdentityAlias1, size_t);
+void *operator new(TypeIdentityAlias1, size_t); // #19
 #if defined(NO_TAA)
-//expected-error at -2 {{type aware allocation operators are disabled}}
+//expected-error@#19 {{type aware allocation operators are disabled}}
 #endif
 
 using TypeIdentityAlias2 = std::type_identity<double>;
-void *operator new(TypeIdentityAlias2, size_t);
+void *operator new(TypeIdentityAlias2, size_t); // #20
 #if defined(NO_TAA)
-//expected-error at -2 {{type aware allocation operators are disabled}}
+//expected-error@#20 {{type aware allocation operators are disabled}}
 #endif
 
 template <typename T> using TypeIdentityAlias3 = std::type_identity<T>;
-template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t);
+template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t); // #21
 #if defined(NO_TAA)
-//expected-error at -2 {{type aware allocation operators are disabled}}
+//expected-error@#21 {{type aware allocation operators are disabled}}
 #endif
 
 
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
index a80a7cc5a9f3a6..eb8acf86c9e763 100644
--- 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
@@ -12,53 +12,53 @@ using size_t = __SIZE_TYPE__;
 
 // Basic valid declarations
 struct S {
-  void *operator new(std::type_identity<S>, size_t);
-  void operator delete(std::type_identity<S>, void *);
+  void *operator new(std::type_identity<S>, size_t); // #1
+  void operator delete(std::type_identity<S>, void *); // #2
 #if defined(NO_TAA)
-  //expected-error at -3 {{type aware allocation operators are disabled}}
-  //expected-error at -3 {{type aware allocation operators are disabled}}
+  //expected-error@#1 {{type aware allocation operators are disabled}}
+  //expected-error@#2 {{type aware allocation operators are disabled}}
 #endif
   void operator delete(S *, std::destroying_delete_t);
 };
 
 template <typename T> struct S2 {
-  void *operator new(std::type_identity<S2<T>>, size_t);
-  void operator delete(std::type_identity<S2<T>>, void *);
+  void *operator new(std::type_identity<S2<T>>, size_t); // #3
+  void operator delete(std::type_identity<S2<T>>, void *); // #4
 #if defined(NO_TAA)
-  //expected-error at -3 {{type aware allocation operators are disabled}}
-  //expected-error at -3 {{type aware allocation operators are disabled}}
+  //expected-error@#3 {{type aware allocation operators are disabled}}
+  //expected-error@#4 {{type aware allocation operators are disabled}}
 #endif
   void operator delete(S2 *, std::destroying_delete_t);
 };
 
 struct S3 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  template <typename T> void operator delete(std::type_identity<T>, void *);
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #5
+  template <typename T> void operator delete(std::type_identity<T>, void *); // #6
 #if defined(NO_TAA)
-  //expected-error at -3 {{type aware allocation operators are disabled}}
-  //expected-error at -3 {{type aware allocation operators are disabled}}
+  //expected-error@#5 {{type aware allocation operators are disabled}}
+  //expected-error@#6 {{type aware allocation operators are disabled}}
 #endif
   void operator delete(S3 *, std::destroying_delete_t);
 };
 
 struct S4 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  template <typename T> void operator delete(std::type_identity<T>, void *);
-  template <typename T> void operator delete(std::type_identity<T>, S4 *, std::destroying_delete_t); // #1
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #7
+  template <typename T> void operator delete(std::type_identity<T>, void *); // #8
+  template <typename T> void operator delete(std::type_identity<T>, S4 *, std::destroying_delete_t); // #9
 #if defined(NO_TAA)
-  //expected-error at -4 {{type aware allocation operators are disabled}}
-  //expected-error at -4 {{type aware allocation operators are disabled}}
-  //expected-error at -4 {{type aware allocation operators are disabled}}
+  //expected-error@#7 {{type aware allocation operators are disabled}}
+  //expected-error@#8 {{type aware allocation operators are disabled}}
+  //expected-error@#9 {{type aware allocation operators are disabled}}
 #elif defined(NO_TADD)
-  // expected-error@#1 {{type aware destroying delete is not permitted}}
+  // expected-error@#9 {{type aware destroying delete is not permitted}}
 #endif
 };
 
 struct S5 {
-  template <typename T> void operator delete(std::type_identity<T>, T *); // #2
+  template <typename T> void operator delete(std::type_identity<T>, T *); // #10
 #if defined(NO_TAA)
-  // expected-error@#2 {{type aware allocation operators are disabled}}
+  // expected-error@#10 {{type aware allocation operators are disabled}}
 #else
-  // expected-error@#2 {{'operator delete' cannot take a dependent type as first parameter; use 'void *'}}
+  // expected-error@#10 {{'operator delete' cannot take a dependent type as first parameter; use 'void *'}}
 #endif
 };
diff --git a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
index 62f1672fd90b32..df8add88ab04f5 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -21,128 +21,123 @@ void *operator new(size_t, std::align_val_t);
 void operator delete(void *);
 
 struct UntypedInclassNew {
-  void *operator new(size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
-  void  operator delete(void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  void *operator new(size_t) = delete; // #1
+  void  operator delete(void *) = delete; //#2
 };
-void *operator new(std::type_identity<UntypedInclassNew>, size_t); // expected-note {{candidate function not viable}}
-void  operator delete(std::type_identity<UntypedInclassNew>, void*);
+void *operator new(std::type_identity<UntypedInclassNew>, size_t); // #3
+void  operator delete(std::type_identity<UntypedInclassNew>, void*); // #4
+
 
 struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_NoAlignedAlloc {
-  void *operator new(size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
-  void  operator delete(void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  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); // expected-note {{candidate function not viable}}
-void operator delete(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, void *, std::align_val_t);
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, size_t, std::align_val_t); // #7
+void operator delete(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, void *, std::align_val_t); // #8
 
 struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_AlignedAlloc {
-  void *operator new(size_t, std::align_val_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
-  void  operator delete(void *, std::align_val_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  void *operator new(size_t, std::align_val_t) = delete; // #9
+  void  operator delete(void *, std::align_val_t) = delete; // #10
 };
-void *operator new(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, size_t, std::align_val_t); // expected-note {{candidate function not viable}}
-void  operator delete(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, void *, std::align_val_t);
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, size_t, std::align_val_t); // #11
+void  operator delete(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, void *, std::align_val_t); // #12
 
 struct BasicClass {};
-void *operator new(std::type_identity<BasicClass>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
-void  operator delete(std::type_identity<BasicClass>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+void *operator new(std::type_identity<BasicClass>, size_t) = delete; // #13
+void  operator delete(std::type_identity<BasicClass>, void *) = delete; // #14
 
 struct InclassNew1 {
-  void *operator new(std::type_identity<InclassNew1>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
-  void  operator delete(std::type_identity<InclassNew1>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  void *operator new(std::type_identity<InclassNew1>, size_t) = delete; // #15
+  void  operator delete(std::type_identity<InclassNew1>, void *) = delete; // #16
 };
-void *operator new(std::type_identity<InclassNew1>, size_t); // expected-note {{candidate function not viable}}
-void  operator delete(std::type_identity<InclassNew1>, void *);
+void *operator new(std::type_identity<InclassNew1>, size_t); // #17
+void  operator delete(std::type_identity<InclassNew1>, void *); // #18
 
 struct InclassNew2 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t) = delete; // expected-note {{candidate function [with T = InclassNew2] has been explicitly deleted}}
-  template <typename T> void  operator delete(std::type_identity<T>, void *) = delete; // expected-note {{'operator delete<InclassNew2>' has been explicitly marked deleted here}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t) = delete; // #19
+  template <typename T> void  operator delete(std::type_identity<T>, void *) = delete; // #20
 };
-void *operator new(std::type_identity<InclassNew2>, size_t); // expected-note {{candidate function not viable}}
-void  operator delete(std::type_identity<InclassNew2>, void *);
+void *operator new(std::type_identity<InclassNew2>, size_t); // #21
+void  operator delete(std::type_identity<InclassNew2>, void *); // #22
 
 struct InclassNew3 {
-  void *operator new(std::type_identity<InclassNew3>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
-  void  operator delete(std::type_identity<InclassNew3>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
-  template <typename T> void *operator new(std::type_identity<T>, size_t); // expected-note {{candidate function [with T = InclassNew3]}}
-  template <typename T> void  operator delete(std::type_identity<T>, void *);
+  void *operator new(std::type_identity<InclassNew3>, size_t) = delete; // #23
+  void  operator delete(std::type_identity<InclassNew3>, void *) = delete; // #24
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #25
+  template <typename T> void  operator delete(std::type_identity<T>, void *); // #26
 };
 
 struct __attribute__((aligned(128))) InclassNew4 {
-  void *operator new(std::type_identity<InclassNew4>, size_t); // expected-note {{candidate function not viable}}
-  void  operator delete(std::type_identity<InclassNew4>, void *);
-  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // expected-note {{candidate function [with T = InclassNew4] has been explicitly deleted}}
-  template <typename T> void  operator delete(std::type_identity<T>, void *, std::align_val_t) = delete; // expected-note {{'operator delete<InclassNew4>' has been explicitly marked deleted here}}
+  void *operator new(std::type_identity<InclassNew4>, size_t); // #27
+  void  operator delete(std::type_identity<InclassNew4>, void *); // #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 *, std::align_val_t) = delete; // #30
 };
 
 struct InclassNew5 {
   InclassNew5();
-  void *operator new(std::type_identity<InclassNew5>, size_t);
-  void  operator delete(void *);
-  void  operator delete(std::type_identity<InclassNew5>, void *) = delete; // expected-note 2 {{'operator delete' has been explicitly marked deleted here}}
+  void *operator new(std::type_identity<InclassNew5>, size_t); // #31
+  void  operator delete(void *); // #32
+  void  operator delete(std::type_identity<InclassNew5>, void *) = delete; // #33
 };
 
 struct InclassNew6 {
   InclassNew6();
-  void *operator new(size_t); // expected-note {{non-type aware 'operator new' declared here}}
-  void  operator delete(void *) = delete;
-  void  operator delete(std::type_identity<InclassNew6>, void *) = delete; // expected-note 2 {{'operator delete' has been explicitly marked deleted here}}
-  // expected-note at -1 {{type aware 'operator delete' declared here}}
+  void *operator new(size_t); // #34
+  void  operator delete(void *) = delete; // #35
+  void  operator delete(std::type_identity<InclassNew6>, void *) = delete; // #36
 };
 
 struct InclassNew7 {
   InclassNew7();
-  void *operator new(std::type_identity<InclassNew7>, size_t);
-  void  operator delete(std::type_identity<InclassNew7>, void *);
-  void  operator delete(InclassNew7 *, std::destroying_delete_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+  void *operator new(std::type_identity<InclassNew7>, size_t); // #37
+  void  operator delete(std::type_identity<InclassNew7>, void *); // #38
+  void  operator delete(InclassNew7 *, std::destroying_delete_t) = delete; // #39
 };
 
 struct InclassNew8 {
   InclassNew8();
-  void *operator new(std::type_identity<InclassNew8>, size_t);  // expected-note {{type aware 'operator new' declared here}}
-  void operator delete(void*);  // expected-note {{non-type aware 'operator delete' declared here}}
+  void *operator new(std::type_identity<InclassNew8>, size_t); // #40
+  void operator delete(void*); // #41
 };
 
 struct InclassNew9 {
   InclassNew9();
-  void *operator new(std::type_identity<InclassNew9>, size_t);
-  // expected-note at -1 {{type aware 'operator new' found in 'InclassNew9'}}
+  void *operator new(std::type_identity<InclassNew9>, size_t); // #42
 };
 
-void operator delete(std::type_identity<InclassNew9>, void*);
-// expected-note at -1 {{type aware 'operator delete' found in the global namespace}}
+void operator delete(std::type_identity<InclassNew9>, void*); // #43
 
 struct BaseClass1 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  template <typename T> void operator delete(std::type_identity<T>, void*) = delete;
-  // expected-note at -1 2 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
-  virtual ~BaseClass1(); // expected-note {{overridden virtual function is here}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #44
+  template <typename T> void operator delete(std::type_identity<T>, void*) = delete; // #45
+  virtual ~BaseClass1();
 };
 
 struct SubClass1 : BaseClass1 { 
-  // expected-error at -1 {{deleted function '~SubClass1' cannot override a non-deleted function}}
-  // expected-note at -2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+  virtual ~SubClass1();
 };
 
 struct BaseClass2 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  template <typename T> void operator delete(std::type_identity<T>, void*) = delete;
-  // expected-note at -1 {{'operator delete<SubClass2>' has been explicitly marked deleted here}}
-  void operator delete(BaseClass2 *, std::destroying_delete_t); 
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #46
+  template <typename T> void operator delete(std::type_identity<T>, void*) = delete; // #47
+  void operator delete(BaseClass2 *, std::destroying_delete_t);  // #48
   virtual ~BaseClass2();
 };
 
 struct SubClass2 : BaseClass2 {
   SubClass2(); // Force exception cleanup which should invoke type aware delete
+  virtual ~SubClass2();
 };
 
 struct BaseClass3 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  template <typename T> void operator delete(std::type_identity<T>, void*);
-  void operator delete(BaseClass3 *, std::destroying_delete_t) = delete;  // expected-note {{'operator delete' has been explicitly marked deleted here}}
-  virtual ~BaseClass3(); // expected-note {{overridden virtual function is here}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #49
+  template <typename T> void operator delete(std::type_identity<T>, void*); // #50
+  void operator delete(BaseClass3 *, std::destroying_delete_t) = delete; // #51
+  virtual ~BaseClass3();
 };
 struct SubClass3 : BaseClass3 {
-  // expected-error at -1 {{deleted function '~SubClass3' cannot override a non-deleted function}}
-  // expected-note at -2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+  virtual ~SubClass3();
 };
 
 template <typename A, typename B> concept Derived = requires (A * a, B *b) { a = b; };
@@ -150,23 +145,18 @@ template <typename A, typename B> concept Same = requires (std::type_identity<A>
 
 struct SubClass4;
 struct BaseClass4 {
-  template <Derived<SubClass4> T> void *operator new(std::type_identity<T>, size_t) = delete;
-  // expected-note at -1 {{candidate function [with T = SubClass4] has been explicitly deleted}}
-  template <Derived<SubClass4> T> void operator delete(std::type_identity<T>, void*) = delete;
-  // expected-note at -1 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  // expected-note at -1 {{candidate function [with T = SubClass4]}}
-  template <typename T> void operator delete(std::type_identity<T>, void*);
-
-  virtual ~BaseClass4(); // expected-note {{overridden virtual function is here}}
+  template <Derived<SubClass4> T> void *operator new(std::type_identity<T>, size_t) = delete; // #52
+  template <Derived<SubClass4> T> void operator delete(std::type_identity<T>, void*) = delete; // #53
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #54
+  template <typename T> void operator delete(std::type_identity<T>, void*); // #55
+
+  virtual ~BaseClass4();
 };
 
 struct SubClass4 : BaseClass4 {
-  // expected-error at -1 {{deleted function '~SubClass4' cannot override a non-deleted function}}
-  // expected-note at -2 2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+  virtual ~SubClass4();
 };
 struct SubClass4_1 : SubClass4 {
-  // expected-note at -1 {{destructor of 'SubClass4_1' is implicitly deleted because base class 'SubClass4' has a deleted destructor}}
   SubClass4_1();
 };
 struct SubClass4_2 : BaseClass4 {
@@ -174,11 +164,10 @@ struct SubClass4_2 : BaseClass4 {
 
 struct SubClass5;
 struct BaseClass5 {
-  template <Same<SubClass5> T> void *operator new(std::type_identity<T>, size_t);
-  template <Same<SubClass5> T> void operator delete(std::type_identity<T>, void*); // expected-note {{member 'operator delete' declared here}}
-  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void *operator new(std::type_identity<T>, size_t) = delete;
-  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void operator delete(std::type_identity<T>, void*) = delete;
-  // expected-note at -1 {{member 'operator delete' declared here}}
+  template <Same<SubClass5> T> void *operator new(std::type_identity<T>, size_t); // #56
+  template <Same<SubClass5> T> void operator delete(std::type_identity<T>, void*); // #57
+  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void *operator new(std::type_identity<T>, size_t) = delete; // #58
+  template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void operator delete(std::type_identity<T>, void*) = delete; // #59
 };
 
 struct SubClass5 : BaseClass5 {
@@ -188,22 +177,18 @@ struct SubClass5_1 : SubClass5 {
 
 
 struct BaseClass6 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  // expected-note at -1 {{type aware 'operator new' found in 'BaseClass6'}}
-  template <typename T> void operator delete(std::type_identity<T>, void*);
-  // expected-note at -1 {{type aware 'operator delete' found in 'BaseClass6'}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #60
+  template <typename T> void operator delete(std::type_identity<T>, void*); // #61
   BaseClass6();
   virtual ~BaseClass6();
 };
 
 struct SubClass6_1 : BaseClass6 {
-  template <typename T> void *operator new(std::type_identity<T>, size_t);
-  // expected-note at -1 {{type aware 'operator new' found in 'SubClass6_1'}}
+  template <typename T> void *operator new(std::type_identity<T>, size_t); // #62
   SubClass6_1();
 };
 struct SubClass6_2 : BaseClass6 {
-  template <typename T> void operator delete(std::type_identity<T>, void*);
-  // expected-note at -1 {{type aware 'operator delete' found in 'SubClass6_2'}}
+  template <typename T> void operator delete(std::type_identity<T>, void*); // #63
   SubClass6_2();
 };
 
@@ -211,77 +196,148 @@ struct SubClass6_2 : BaseClass6 {
 void test() {
   
   // untyped in class declaration wins
-  UntypedInclassNew *O1 = new UntypedInclassNew; // expected-error {{call to deleted function 'operator new'}}
-  delete O1; // expected-error {{attempt to use a deleted function}}
+  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 {{call to deleted function 'operator new'}}
-  delete O2; // expected-error {{attempt to use a deleted function}}
+  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 {{call to deleted function 'operator new'}}
-  delete O3; // expected-error {{attempt to use a deleted function}}
+  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 {{call to deleted function 'operator new'}}
-  delete O4; // expected-error {{attempt to use a deleted function}}
+  BasicClass *O4 = new BasicClass;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#13 {{candidate function has been explicitly deleted}}
+  delete O4;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#14 {{'operator delete' has been explicitly marked deleted here}}
+  // 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: requires 3 arguments, but 2 were provided}}
+  // expected-note@#11 {{candidate function not viable: requires 3 arguments, but 2 were provided}}
 
   // We resolve the explicitly typed in class operator
-  InclassNew1 *O5 = new InclassNew1; // expected-error {{call to deleted function 'operator new'}}
-  delete O5; // expected-error {{attempt to use a deleted function}}
+  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 {{call to deleted function 'operator new'}}
-  delete O6; // expected-error {{attempt to use a deleted function}}
+  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 {{call to deleted function 'operator new'}}
-  delete O7; // expected-error {{attempt to use a deleted function}}
+  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}}
 
   // We prefer the aligned but unconstrained operators over the unaligned but constrained variants
-  InclassNew4 *O8 = new InclassNew4; // expected-error {{call to deleted function 'operator new'}}
-  delete O8; // expected-error {{attempt to use a deleted function}}
+  InclassNew4 *O8 = new InclassNew4;
+  // expected-error at -1 {{call to deleted function 'operator new'}}
+  // expected-note@#29 {{candidate function [with T = InclassNew4] has been explicitly deleted}}
+  // expected-note@#27 {{candidate function not viable: requires 2 arguments, but 3 were provided}}
+
+  delete O8;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#30 {{'operator delete<InclassNew4>' has been explicitly marked deleted here}}
 
   // Constructor clean up invokes typed operator if typed new was used
-  InclassNew5 *O9 = new InclassNew5; // expected-error {{attempt to use a deleted function}}
-  delete O9; // expected-error {{attempt to use a deleted function}}
+  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}}
 
   // Are these reasonable? Should we ensure that declaration of new vs delete have consistent type
   // semantics? How do we define consistent?
   // Constructor clean up invokes untyped delete if untyped delete was used
-  InclassNew6 *O10 = new InclassNew6; // expected-error {{attempt to use a deleted function}}
-  // expected-warning at -1 {{mismatching type aware allocation operators for constructor cleanup}}
-  delete O10; //expected-error {{attempt to use a deleted function}}
+  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-warning at -3 {{mismatched type aware allocation operators for constructor cleanup}}
+  // expected-note@#34 {{non-type aware 'operator new' declared here}}
+  // 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 {{attempt to use a deleted function}}
+  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-warning at -1 {{mismatching type aware allocation operators for constructor cleanup}}
+  // expected-warning at -1 {{mismatched type aware allocation operators for constructor cleanup}}
+  // expected-note@#40 {{type aware 'operator new' declared here}}
+  // 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 matching 'operator delete' in 'InclassNew9'}}
+  // expected-note@#42 {{type aware 'operator new' found in 'InclassNew9'}}
+  // expected-note@#43 {{type aware 'operator delete' found in the global namespace}}
+
   delete O13;
 
   // Creating the virtual destructor for an type requires the deleting destructor
   // for that type
-  SubClass1 *O14 = new SubClass1; // expected-error {{attempt to use a deleted function}}
-  delete O14; // expected-error {{attempt to use a deleted function}}
+  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 {{attempt to use a deleted function}}
-  delete O15; 
+  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 {{attempt to use a deleted function}}
-
-  SubClass4 *O17 = new SubClass4; // expected-error {{call to deleted function 'operator new'}}
-  delete O17; // expected-error {{attempt to use a deleted function}}
+  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; // expected-error {{attempt to use a deleted function}}
+  delete O18;
 
   SubClass4_2 *O19 = new SubClass4_2;
   delete O19;
@@ -289,14 +345,22 @@ void test() {
   SubClass5 *O20 = new SubClass5;
   delete O20;
 
-  SubClass5_1 *O21 = new SubClass5_1; // expected-error {{no matching function for call to 'operator new'}}
-  delete O21; // expected-error {{no suitable member 'operator delete' in 'SubClass5_1'}}
+  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 matching 'operator delete' in 'SubClass6_1'}}
+  // expected-error at -1 {{type aware 'operator new<SubClass6_1>' requires matching 'operator delete' in 'SubClass6_1'}}
+  // expected-note@#62 {{type aware 'operator new<SubClass6_1>' found in 'SubClass6_1'}}
+  // expected-note@#61 {{type aware 'operator delete' found in 'BaseClass6'}}
   delete O22;
 
   SubClass6_2 *O23 = new SubClass6_2;
-  // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'BaseClass6'}}
+  // expected-error at -1 {{type aware 'operator new<SubClass6_2>' requires matching 'operator delete' in 'BaseClass6'}}
+  // expected-note@#60 {{type aware 'operator new<SubClass6_2>' found in 'BaseClass6'}}
+  // expected-note@#63 {{type aware 'operator delete' found in 'SubClass6_2'}}
   delete O23;
 }

>From 74a9cf0b29d864d90523d5ccf53ac5cb08a3afcd Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 9 Nov 2024 01:43:55 -0800
Subject: [PATCH 6/6] Yet more review comments

---
 clang/include/clang/Basic/DiagnosticGroups.td    |  4 ----
 clang/include/clang/Basic/DiagnosticSemaKinds.td |  2 +-
 clang/include/clang/Sema/Sema.h                  |  5 +++--
 clang/lib/AST/Type.cpp                           |  2 +-
 clang/lib/CodeGen/CGExprCXX.cpp                  | 10 +++++-----
 clang/lib/Sema/SemaDeclCXX.cpp                   | 15 ++++++++++-----
 clang/lib/Sema/SemaExprCXX.cpp                   |  2 +-
 7 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 7fa46023593466..72eada50a56cc9 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1524,10 +1524,6 @@ in addition with the pragmas or -fmax-tokens flag to get any warnings.
 }];
 }
 
-// Warning group for type aware allocators
-def TypeAwareAllocatorMismatch :
-  DiagGroup<"type-aware-allocator-mismatch">;
-
 def WebAssemblyExceptionSpec : DiagGroup<"wasm-exception-spec">;
 
 def RTTI : DiagGroup<"rtti">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1989e10906e584..cf7fd31b0ea552 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9717,7 +9717,7 @@ def err_type_aware_operator_found : Note<
   "type aware %0 found in %1">;
 def warn_mismatching_type_aware_cleanup_deallocator : Warning<
   "mismatched type aware allocation operators for constructor cleanup">,
-  InGroup<TypeAwareAllocatorMismatch>;
+  InGroup<DiagGroup<"type-aware-allocator-mismatch">>;
 def note_type_aware_operator_declared : Note<
   "%select{|non-}0type aware %1 declared here">;
 def note_implicit_delete_this_in_destructor_here : Note<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4b649b3b8bf234..bd66a25a0a01f2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4757,7 +4757,8 @@ class Sema final : public SemaBase {
 
   CXXRecordDecl *getStdBadAlloc() const;
   EnumDecl *getStdAlignValT() const;
-  ClassTemplateDecl *getStdTypeIdentity() const;
+  const ClassTemplateDecl *getStdTypeIdentity() const;
+  ClassTemplateDecl *getStdTypeIdentity();
   std::optional<QualType> instantiateSpecializedTypeIdentity(QualType Subject);
   bool isTypeIdentitySpecialization(QualType Type) const;
   bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const;
@@ -8146,7 +8147,7 @@ class Sema final : public SemaBase {
 
   /// The scope in which to find allocation functions.
   enum AllocationFunctionScope {
-    /// Only look for allocation functions in the global scope
+    /// Only look for allocation functions in the global scope.
     AFS_Global,
     /// Only look for allocation functions in the scope of the
     /// allocated class.
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 726ec70609804d..cfb2e7ea8c7989 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3103,7 +3103,7 @@ const TemplateDecl *Type::getSpecializedTemplateDecl() const {
   if (const auto *Specialization = DesugaredType->getAs<TemplateSpecializationType>())
     return Specialization->getTemplateName().getAsTemplateDecl();
   if (const auto *Record = DesugaredType->getAsCXXRecordDecl()) {
-    if (auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Record))
+    if (const auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Record))
       return CTS->getSpecializedTemplate();
   }
   return nullptr;
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index dae8dc12c1e0ea..4e6fdd475e59f7 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1395,7 +1395,7 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
 
   if (FD->isTypeAwareOperatorNewOrDelete()) {
     // Assume Sema has ensured a non-pointer first parameter is
-    // a type identity
+    // a type identity.
     Params.TypedAwareDelete = true;
     assert(AI != AE);
     ++AI;
@@ -1441,7 +1441,7 @@ namespace {
       QualType ArgType;
     };
 
-    unsigned NumPlacementArgs : 31;
+    unsigned NumPlacementArgs : 30;
     LLVM_PREFERRED_TYPE(bool)
     unsigned PassAlignmentToPlacementDelete : 1;
     LLVM_PREFERRED_TYPE(bool)
@@ -1829,7 +1829,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   auto Params = getUsualDeleteParams(DeleteFD);
   auto ParamTypeIt = DeleteFTy->param_type_begin();
 
-  llvm::AllocaInst *TypeIdentityag = nullptr;
+  llvm::AllocaInst *TypeIdentityArg = nullptr;
   if (Params.TypedAwareDelete) {
     QualType SpecializedTypeIdentity = *ParamTypeIt++;
     CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
@@ -1890,8 +1890,8 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   // Emit the call to delete.
   EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs);
 
-  if (TypeIdentityag && TypeIdentityag->use_empty())
-    TypeIdentityag->eraseFromParent();
+  if (TypeIdentityArg && TypeIdentityArg->use_empty())
+    TypeIdentityArg->eraseFromParent();
 
   // If call argument lowering didn't use the destroying_delete_t alloca,
   // remove it again.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3ce4b65deffd39..fea48ce230761e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11740,7 +11740,12 @@ NamespaceDecl *Sema::getStdNamespace() const {
                                  StdNamespace.get(Context.getExternalSource()));
 }
 
-ClassTemplateDecl *Sema::getStdTypeIdentity() const {
+const ClassTemplateDecl *Sema::getStdTypeIdentity() const {
+  return cast_or_null<ClassTemplateDecl>(
+      StdTypeIdentity.get(Context.getExternalSource()));
+}
+
+ClassTemplateDecl *Sema::getStdTypeIdentity() {
   return cast_or_null<ClassTemplateDecl>(
       StdTypeIdentity.get(Context.getExternalSource()));
 }
@@ -16124,7 +16129,7 @@ bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
 }
 
 bool Sema::isTypeIdentitySpecialization(QualType Type) const {
-  ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
+  const ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
   if (!TypeIdentity)
     return false;
   const TemplateDecl *SpecializedDecl = Type->getSpecializedTemplateDecl();
@@ -16175,7 +16180,7 @@ Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
 
   for (size_t Idx = 1; Idx < NumParams; ++Idx) {
     // A type aware allocation is only usual if the only dependent parameter is
-    // the first parameter
+    // the first parameter.
     const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(Idx);
     if (ParamDecl->getType()->isDependentType())
       return std::nullopt;
@@ -16231,9 +16236,9 @@ std::optional<QualType>
 Sema::instantiateSpecializedTypeIdentity(QualType Subject) {
   assert(AllowTypeAwareAllocators());
   ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
-  if (!TypeIdentity) {
+  if (!TypeIdentity)
     return std::nullopt;
-  }
+
   auto TN = TemplateName(TypeIdentity);
   TemplateArgumentListInfo Arguments;
   Arguments.addArgument(getTrivialTemplateArgumentLoc(
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e8059c826ec9ec..f95821690ff760 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1781,7 +1781,7 @@ namespace {
           Destroying(false), HasSizeT(false), HasAlignValT(false),
           HasTypeIdentity(false), CUDAPref(SemaCUDA::CFP_Native) {
       // A function template declaration is only a usual deallocation function
-      // if it is a typed delete
+      // if it is a typed delete.
       if (!FD) {
         auto *FTD = dyn_cast<FunctionTemplateDecl>(Found->getUnderlyingDecl());
         if (!FTD)



More information about the cfe-commits mailing list