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

Oliver Hunt via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 18 00:52:39 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 01/18] 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 02/18] 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 03/18] 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 04/18] 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 05/18] 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 06/18] 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)

>From 526183769479e23ecd3891a8785f223b80ddac26 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 9 Nov 2024 17:54:13 -0800
Subject: [PATCH 07/18] Minor edits

---
 clang/include/clang/Driver/Options.td                       | 6 +++---
 clang/lib/Driver/ToolChains/Clang.cpp                       | 2 +-
 clang/lib/Sema/SemaExprCXX.cpp                              | 6 ++++--
 .../type-aware-new-delete-basic-in-class-declarations.cpp   | 2 +-
 .../test/SemaCXX/type-aware-new-delete-basic-resolution.cpp | 6 +++---
 5 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c4ff4dd75bbdc2..e386c3abec4ca7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3545,11 +3545,11 @@ 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">,
+  PosFlag<SetTrue, [], [ClangOption], "Enable experimental C++YY type aware allocator operators">,
   NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;
-defm cxx_type_aware_destroying_delete : BoolFOption<"cxx-type-aware-destroying-delete",
+defm cxx_type_aware_destroying_delete : BoolFOption<"experimental-cxx-type-aware-destroying-delete",
   LangOpts<"TypeAwareDestroyingDelete">, DefaultFalse,
-  PosFlag<SetTrue, [], [ClangOption], "Enable C++YY type aware allocator operators">,
+  PosFlag<SetTrue, [], [ClangOption], "Enable experimental C++YY type aware allocator operators for destroying delete">,
   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>;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 2396847f41b6b2..f68afa4d4f7000 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7153,7 +7153,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
 
   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");
+    CmdArgs.push_back("-fexperimental-cxx-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/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f95821690ff760..f1592829f0ed69 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2967,8 +2967,10 @@ bool Sema::FindAllocationFunctions(
     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";
@@ -3171,7 +3173,7 @@ bool Sema::FindAllocationFunctions(
           << NewDiagIndex << OperatorNew;
       Diag(OperatorDelete->getLocation(),
            diag::note_type_aware_operator_declared)
-          << DeleteDiagIndex << OperatorDelete->getDeclName();
+          << DeleteDiagIndex << OperatorDelete;
     }
     if (IAP.PassTypeIdentity &&
         OperatorDelete->getDeclContext() != OperatorNew->getDeclContext()) {
@@ -3182,7 +3184,7 @@ bool Sema::FindAllocationFunctions(
       Diag(OperatorNew->getLocation(), diag::err_type_aware_operator_found)
           << OperatorNew << OperatorNew->getDeclContext();
       Diag(OperatorDelete->getLocation(), diag::err_type_aware_operator_found)
-          << OperatorDelete->getDeclName() << OperatorDelete->getDeclContext();
+          << OperatorDelete << OperatorDelete->getDeclContext();
     }
 
     // C++1z [expr.new]p23:
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 eb8acf86c9e763..7aa9ebc98372f1 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
@@ -1,5 +1,5 @@
 // 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           -std=c++17    -fexperimental-cxx-type-aware-allocators -fexperimental-cxx-type-aware-destroying-delete
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TAA  -std=c++17 -fno-experimental-cxx-type-aware-allocators
 
 namespace std {
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 df8add88ab04f5..7a3c224c74a04e 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -1,5 +1,5 @@
 // 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
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DTADD -std=c++2c -fexperimental-cxx-type-aware-allocators -fexperimental-cxx-type-aware-destroying-delete -fexceptions
 
 namespace std {
   template <class T> struct type_identity {};
@@ -355,12 +355,12 @@ void test() {
   SubClass6_1 *O22 = new 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'}}
+  // expected-note@#61 {{type aware 'operator delete<SubClass6_1>' found in 'BaseClass6'}}
   delete O22;
 
   SubClass6_2 *O23 = new SubClass6_2;
   // 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'}}
+  // expected-note@#63 {{type aware 'operator delete<SubClass6_2>' found in 'SubClass6_2'}}
   delete O23;
 }

>From 80154c2b3441f33d609b52463114b7ee5f662798 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 9 Nov 2024 19:19:37 -0800
Subject: [PATCH 08/18] Missed a -fexperimental change and adding flag names to
 diagnostics

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +-
 ...are-new-delete-basic-free-declarations.cpp | 42 +++++++++----------
 ...new-delete-basic-in-class-declarations.cpp | 22 +++++-----
 3 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cf7fd31b0ea552..5c631846042514 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9708,9 +9708,9 @@ 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">;
+  "type aware destroying delete is not permitted, enable with '-fexperimental-cxx-type-aware-destroying-delete'">;
 def err_unsupported_type_aware_allocator : Error<
-  "type aware allocation operators are disabled">;
+  "type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'">;
 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<
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 b842ffcead91b5..6fe2438af01813 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
@@ -22,14 +22,14 @@ template <typename T> void *operator new(std::type_identity<TemplateTestType<T>>
 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@#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}}
+//expected-error@#1 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#2 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#3 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#4 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#5 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#6 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#7 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#8 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
 
 void operator delete(std::type_identity<int>, void *); // #9
@@ -37,10 +37,10 @@ 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@#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}}
+//expected-error@#9 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#10 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#11 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#12 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
 
 template <typename T> void operator delete(std::type_identity<T>, void *); // #13
@@ -48,35 +48,35 @@ template <typename T> void operator delete(std::type_identity<T>, void *, std::a
 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@#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}}
+//expected-error@#13 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#14 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#15 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#16 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
 
 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@#17 {{type aware allocation operators are disabled}}
-//expected-error@#18 {{type aware allocation operators are disabled}}
+//expected-error@#17 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+//expected-error@#18 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
 
 typedef std::type_identity<float> TypeIdentityAlias1;
 void *operator new(TypeIdentityAlias1, size_t); // #19
 #if defined(NO_TAA)
-//expected-error@#19 {{type aware allocation operators are disabled}}
+//expected-error@#19 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
 
 using TypeIdentityAlias2 = std::type_identity<double>;
 void *operator new(TypeIdentityAlias2, size_t); // #20
 #if defined(NO_TAA)
-//expected-error@#20 {{type aware allocation operators are disabled}}
+//expected-error@#20 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
 
 template <typename T> using TypeIdentityAlias3 = std::type_identity<T>;
 template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t); // #21
 #if defined(NO_TAA)
-//expected-error@#21 {{type aware allocation operators are disabled}}
+//expected-error@#21 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #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 7aa9ebc98372f1..d3b4264ff3e727 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
@@ -15,8 +15,8 @@ struct S {
   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@#1 {{type aware allocation operators are disabled}}
-  //expected-error@#2 {{type aware allocation operators are disabled}}
+  //expected-error@#1 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+  //expected-error@#2 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
   void operator delete(S *, std::destroying_delete_t);
 };
@@ -25,8 +25,8 @@ template <typename T> struct S2 {
   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@#3 {{type aware allocation operators are disabled}}
-  //expected-error@#4 {{type aware allocation operators are disabled}}
+  //expected-error@#3 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+  //expected-error@#4 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
   void operator delete(S2 *, std::destroying_delete_t);
 };
@@ -35,8 +35,8 @@ struct S3 {
   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@#5 {{type aware allocation operators are disabled}}
-  //expected-error@#6 {{type aware allocation operators are disabled}}
+  //expected-error@#5 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+  //expected-error@#6 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #endif
   void operator delete(S3 *, std::destroying_delete_t);
 };
@@ -46,18 +46,18 @@ struct S4 {
   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@#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}}
+  //expected-error@#7 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+  //expected-error@#8 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+  //expected-error@#9 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #elif defined(NO_TADD)
-  // expected-error@#9 {{type aware destroying delete is not permitted}}
+  // expected-error@#9 {{type aware destroying delete is not permitted, enable with '-fexperimental-cxx-type-aware-destroying-delete'}}
 #endif
 };
 
 struct S5 {
   template <typename T> void operator delete(std::type_identity<T>, T *); // #10
 #if defined(NO_TAA)
-  // expected-error@#10 {{type aware allocation operators are disabled}}
+  // expected-error@#10 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #else
   // expected-error@#10 {{'operator delete' cannot take a dependent type as first parameter; use 'void *'}}
 #endif

>From 48637e2db55bc4abec94570b9ebeb3073c67939c Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 9 Nov 2024 20:50:53 -0800
Subject: [PATCH 09/18] Correct formatting

---
 clang/lib/AST/Type.cpp         |  3 ++-
 clang/lib/Sema/SemaDeclCXX.cpp |  3 ++-
 clang/lib/Sema/SemaExprCXX.cpp | 17 +++++++++--------
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index cfb2e7ea8c7989..04294911465f82 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3100,7 +3100,8 @@ bool Type::isStdByteType() const {
 
 const TemplateDecl *Type::getSpecializedTemplateDecl() const {
   const Type *DesugaredType = getUnqualifiedDesugaredType();
-  if (const auto *Specialization = DesugaredType->getAs<TemplateSpecializationType>())
+  if (const auto *Specialization =
+          DesugaredType->getAs<TemplateSpecializationType>())
     return Specialization->getTemplateName().getAsTemplateDecl();
   if (const auto *Record = DesugaredType->getAsCXXRecordDecl()) {
     if (const auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Record))
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fea48ce230761e..f9f0fd5961c596 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16163,7 +16163,8 @@ Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
   if (!AllowTypeAwareAllocators())
     return std::nullopt;
 
-  TemplateParameterList *TemplateParameters = FnTemplateDecl->getTemplateParameters();
+  TemplateParameterList *TemplateParameters =
+      FnTemplateDecl->getTemplateParameters();
   if (TemplateParameters->hasParameterPack())
     return std::nullopt;
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f1592829f0ed69..b1beb3fd819ffe 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1863,7 +1863,8 @@ namespace {
         // Type aware allocation involves templates so we need to choose
         // the best type
         FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
-        FunctionTemplateDecl *OtherPrimaryTemplate = Other.FD->getPrimaryTemplate();
+        FunctionTemplateDecl *OtherPrimaryTemplate =
+            Other.FD->getPrimaryTemplate();
         if ((!PrimaryTemplate) != (!OtherPrimaryTemplate))
           return OtherPrimaryTemplate ? 1 : -1;
 
@@ -1914,7 +1915,8 @@ static bool CheckDeleteOperator(Sema &S, SourceLocation StartLoc,
                                 CXXRecordDecl *NamingClass, DeclAccessPair Decl,
                                 FunctionDecl *Operator) {
   if (S.isTypeAwareOperatorNewOrDelete(Operator)) {
-    QualType SelectedTypeIdentityParameter = Operator->getParamDecl(0)->getType();
+    QualType SelectedTypeIdentityParameter =
+        Operator->getParamDecl(0)->getType();
     if (S.RequireCompleteType(StartLoc, SelectedTypeIdentityParameter,
                               diag::err_incomplete_type))
       return true;
@@ -3046,8 +3048,8 @@ bool Sema::FindAllocationFunctions(
 
     DeclareGlobalNewDelete();
     DeallocLookupMode LookupMode = OriginalTypeAwareState
-                          ? DeallocLookupMode::OptionallyTyped
-                          : DeallocLookupMode::Untyped;
+                                       ? DeallocLookupMode::OptionallyTyped
+                                       : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                       DeleteName, AllocElemType);
   }
@@ -3179,8 +3181,7 @@ bool Sema::FindAllocationFunctions(
         OperatorDelete->getDeclContext() != OperatorNew->getDeclContext()) {
       Diag(StartLoc,
            diag::err_no_matching_type_aware_cleanup_deallocator_mismatch)
-          << OperatorNew << DeleteName
-          << OperatorNew->getDeclContext();
+          << OperatorNew << DeleteName << OperatorNew->getDeclContext();
       Diag(OperatorNew->getLocation(), diag::err_type_aware_operator_found)
           << OperatorNew << OperatorNew->getDeclContext();
       Diag(OperatorDelete->getLocation(), diag::err_type_aware_operator_found)
@@ -3498,8 +3499,8 @@ FunctionDecl *Sema::FindUsualDeallocationFunction(
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
   DeallocLookupMode LookupMode = AllowTypeAwareAllocators()
-                        ? DeallocLookupMode::OptionallyTyped
-                        : DeallocLookupMode::Untyped;
+                                     ? DeallocLookupMode::OptionallyTyped
+                                     : DeallocLookupMode::Untyped;
   LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                     Name, DeallocType);
 

>From cc2452ccee8496162244384cb44ba323b1ac457b Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sun, 10 Nov 2024 00:32:35 -0800
Subject: [PATCH 10/18] Sigh, let's appease MSVC's refusal to acknowledge
 designated initializers

---
 clang/include/clang/AST/ExprCXX.h |  41 ++++++--
 clang/lib/AST/ExprCXX.cpp         |   4 +-
 clang/lib/Sema/SemaCoroutine.cpp  |  28 +++---
 clang/lib/Sema/SemaDeclCXX.cpp    |   7 +-
 clang/lib/Sema/SemaExprCXX.cpp    | 160 ++++++++++++++++--------------
 5 files changed, 138 insertions(+), 102 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 66f0bd9df6f3b4..7ff710f90fb7c5 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2234,15 +2234,42 @@ enum class CXXNewInitializationStyle {
   Braces
 };
 
+enum class TypeAwareAllocation { Yes, No };
+
+inline TypeAwareAllocation typeAwareAllocation(bool IsTypeAware) {
+  return IsTypeAware ? TypeAwareAllocation::Yes : TypeAwareAllocation::No;
+}
+
+enum class AlignedAllocation { Yes, No };
+
+inline AlignedAllocation alignedAllocation(bool IsAligned) {
+  return IsAligned ? AlignedAllocation::Yes : AlignedAllocation::No;
+}
+
+enum class SizedDeallocation { Yes, No };
+
+inline SizedDeallocation sizedDeallocation(bool IsSized) {
+  return IsSized ? SizedDeallocation::Yes : SizedDeallocation::No;
+}
+
 struct ImplicitAllocationParameters {
-  bool PassTypeIdentity;
-  bool PassAlignment;
+  TypeAwareAllocation PassTypeIdentity;
+  AlignedAllocation PassAlignment;
+  bool passTypeIdentity() const {
+    return PassTypeIdentity == TypeAwareAllocation::Yes;
+  }
+  bool passAlignment() const { return PassAlignment == AlignedAllocation::Yes; }
 };
 
 struct ImplicitDeallocationParameters {
-  bool PassTypeIdentity;
-  bool PassAlignment;
-  bool PassSize;
+  TypeAwareAllocation PassTypeIdentity;
+  AlignedAllocation PassAlignment;
+  SizedDeallocation PassSize;
+  bool passTypeIdentity() const {
+    return PassTypeIdentity == TypeAwareAllocation::Yes;
+  }
+  bool passAlignment() const { return PassAlignment == AlignedAllocation::Yes; }
+  bool passSize() const { return PassSize == SizedDeallocation::Yes; }
 };
 
 /// Represents a new-expression for memory allocation and constructor
@@ -2465,8 +2492,8 @@ class CXXNewExpr final
   /// Provides the full set of information about expected implicit
   /// parameters in this call
   ImplicitAllocationParameters implicitAllocationParameters() const {
-    return ImplicitAllocationParameters{.PassTypeIdentity = passTypeIdentity(),
-                                        .PassAlignment = passAlignment()};
+    return ImplicitAllocationParameters{typeAwareAllocation(passTypeIdentity()),
+                                        alignedAllocation(passAlignment())};
   }
 
   using arg_iterator = ExprIterator;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 7a9de7b9abd3b9..cae5d3d94bae13 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -247,8 +247,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
 
   CXXNewExprBits.IsGlobalNew = IsGlobalNew;
   CXXNewExprBits.IsArray = ArraySize.has_value();
-  CXXNewExprBits.ShouldPassAlignment = IAP.PassAlignment;
-  CXXNewExprBits.ShouldPassTypeIdentity = IAP.PassTypeIdentity;
+  CXXNewExprBits.ShouldPassAlignment = IAP.passAlignment();
+  CXXNewExprBits.ShouldPassTypeIdentity = IAP.passTypeIdentity();
   CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize;
   CXXNewExprBits.HasInitializer = Initializer != nullptr;
   CXXNewExprBits.StoredInitializationStyle =
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index cd9581dc6b85e5..ec3a0e7a941227 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1112,10 +1112,9 @@ 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};
+  ImplicitDeallocationParameters IDP = {
+      typeAwareAllocation(S.AllowTypeAwareAllocators()),
+      alignedAllocation(Overaligned), SizedDeallocation::Yes};
   if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
                                  PromiseType, IDP, /*Diagnose*/ true))
     return false;
@@ -1133,7 +1132,8 @@ 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.
-    IDP.PassSize = CanProvideSize;
+    IDP.PassSize =
+        CanProvideSize ? SizedDeallocation::Yes : SizedDeallocation::No;
     OperatorDelete =
         S.FindUsualDeallocationFunction(PromiseType, Loc, IDP, DeleteName);
 
@@ -1426,8 +1426,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper function to indicate whether the last lookup found the aligned
   // allocation function.
   ImplicitAllocationParameters IAP = {
-      .PassTypeIdentity = S.AllowTypeAwareAllocators(),
-      .PassAlignment = S.getLangOpts().CoroAlignedAllocation != 0};
+      typeAwareAllocation(S.AllowTypeAwareAllocators()),
+      alignedAllocation(S.getLangOpts().CoroAlignedAllocation)};
   auto LookupAllocationFunction =
       [&](Sema::AllocationFunctionScope NewScope = Sema::AFS_Both,
           bool WithoutPlacementArgs = false, bool ForceNonAligned = false) {
@@ -1442,9 +1442,9 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
         if (NewScope == Sema::AFS_Both)
           NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
 
-        IAP = {.PassTypeIdentity = S.AllowTypeAwareAllocators(),
-               .PassAlignment =
-                   !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation};
+        IAP = {typeAwareAllocation(S.AllowTypeAwareAllocators()),
+               alignedAllocation(!ForceNonAligned &&
+                                 S.getLangOpts().CoroAlignedAllocation)};
 
         FunctionDecl *UnusedResult = nullptr;
 
@@ -1478,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 && !IAP.PassAlignment))
+        (S.getLangOpts().CoroAlignedAllocation && !IAP.passAlignment()))
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
                                /*WithoutPlacementArgs*/ true);
   }
@@ -1503,7 +1503,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper variable to emit warnings.
   bool FoundNonAlignedInPromise = false;
   if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
-    if (!OperatorNew || !IAP.PassAlignment) {
+    if (!OperatorNew || !IAP.passAlignment()) {
       FoundNonAlignedInPromise = OperatorNew;
 
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
@@ -1598,7 +1598,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     return false;
 
   SmallVector<Expr *, 3> NewArgs;
-  if (IAP.PassTypeIdentity) {
+  if (IAP.passTypeIdentity()) {
     std::optional<QualType> SpecializedTypeIdentity =
         S.instantiateSpecializedTypeIdentity(PromiseType);
     if (!SpecializedTypeIdentity)
@@ -1612,7 +1612,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     NewArgs.push_back(TypeIdentity.get());
   }
   NewArgs.push_back(FrameSize);
-  if (S.getLangOpts().CoroAlignedAllocation && IAP.PassAlignment)
+  if (S.getLangOpts().CoroAlignedAllocation && IAP.passAlignment())
     NewArgs.push_back(FrameAlignment);
 
   if (OperatorNew->getNumParams() > NewArgs.size())
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f9f0fd5961c596..d419618a57d658 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9714,10 +9714,9 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
     QualType DeallocType = Context.getRecordType(RD);
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
-    ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
-                                              AllowTypeAwareAllocators(),
-                                          .PassAlignment = false,
-                                          .PassSize = false};
+    ImplicitDeallocationParameters IDP = {
+        typeAwareAllocation(AllowTypeAwareAllocators()), AlignedAllocation::No,
+        SizedDeallocation::No};
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
                                  OperatorDelete, DeallocType, IDP,
                                  /*Diagnose*/ false)) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b1beb3fd819ffe..d10b42ea07755a 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1778,8 +1778,10 @@ namespace {
     UsualDeallocFnInfo() : Found(), FD(nullptr) {}
     UsualDeallocFnInfo(Sema &S, DeclAccessPair Found, QualType AllocType)
         : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
-          Destroying(false), HasSizeT(false), HasAlignValT(false),
-          HasTypeIdentity(false), CUDAPref(SemaCUDA::CFP_Native) {
+          Destroying(false), SizedDelete(SizedDeallocation::No),
+          AlignedDelete(AlignedAllocation::No),
+          TypeAwareDelete(TypeAwareAllocation::No),
+          CUDAPref(SemaCUDA::CFP_Native) {
       // A function template declaration is only a usual deallocation function
       // if it is a typed delete.
       if (!FD) {
@@ -1793,8 +1795,8 @@ namespace {
         FD = *InstantiatedDecl;
       }
       unsigned NumBaseParams = 1;
-      if (S.isTypeAwareOperatorNewOrDelete(FD) &&
-          S.AllowTypeAwareAllocators()) {
+      if (S.AllowTypeAwareAllocators() &&
+          S.isTypeAwareOperatorNewOrDelete(FD)) {
         QualType TypeIdentityTag = FD->getParamDecl(0)->getType();
         std::optional<QualType> ExpectedTypeIdentityTag =
             S.instantiateSpecializedTypeIdentity(AllocType);
@@ -1806,7 +1808,7 @@ namespace {
           FD = nullptr;
           return;
         }
-        HasTypeIdentity = true;
+        TypeAwareDelete = TypeAwareAllocation::Yes;
         ++NumBaseParams;
       }
 
@@ -1820,13 +1822,13 @@ namespace {
               FD->getParamDecl(NumBaseParams)->getType(),
               S.Context.getSizeType())) {
         ++NumBaseParams;
-        HasSizeT = true;
+        SizedDelete = SizedDeallocation::Yes;
       }
 
       if (NumBaseParams < FD->getNumParams() &&
           FD->getParamDecl(NumBaseParams)->getType()->isAlignValT()) {
         ++NumBaseParams;
-        HasAlignValT = true;
+        AlignedDelete = AlignedAllocation::Yes;
       }
 
       // In CUDA, determine how much we'd like / dislike to call this.
@@ -1837,6 +1839,12 @@ namespace {
 
     explicit operator bool() const { return FD; }
 
+    bool isTypeAware() const {
+      return TypeAwareDelete == TypeAwareAllocation::Yes;
+    }
+    bool isAligned() const { return AlignedDelete == AlignedAllocation::Yes; }
+    bool isSized() const { return SizedDelete == SizedDeallocation::Yes; }
+
     int Compare(Sema &S, const UsualDeallocFnInfo &Other,
                 ImplicitDeallocationParameters IDP) const {
       // C++ P0722:
@@ -1846,20 +1854,20 @@ namespace {
         return Destroying ? 1 : -1;
 
       // Selection for type awareness has priority over alignment and size
-      if (HasTypeIdentity != Other.HasTypeIdentity)
-        return HasTypeIdentity == IDP.PassTypeIdentity ? 1 : -1;
+      if (isTypeAware() != Other.isTypeAware())
+        return isTypeAware() == 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 == IDP.PassAlignment ? 1 : -1;
+      if (isAligned() != Other.isAligned())
+        return isAligned() == IDP.passAlignment() ? 1 : -1;
 
-      if (HasSizeT != Other.HasSizeT)
-        return HasSizeT == IDP.PassSize ? 1 : -1;
+      if (isSized() != Other.isSized())
+        return isSized() == IDP.passSize() ? 1 : -1;
 
-      if (HasTypeIdentity) {
+      if (isTypeAware()) {
         // Type aware allocation involves templates so we need to choose
         // the best type
         FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
@@ -1873,7 +1881,7 @@ namespace {
           const auto *OtherDC =
               dyn_cast<CXXRecordDecl>(Other.Found->getDeclContext());
           unsigned ImplicitArgCount =
-              1 + Destroying + HasTypeIdentity + HasAlignValT + HasSizeT;
+              1 + Destroying + isTypeAware() + isAligned() + isSized();
           if (FunctionTemplateDecl *Best = S.getMoreSpecializedTemplate(
                   PrimaryTemplate, OtherPrimaryTemplate, SourceLocation(),
                   TPOC_Call, ImplicitArgCount,
@@ -1895,7 +1903,10 @@ namespace {
 
     DeclAccessPair Found;
     FunctionDecl *FD;
-    bool Destroying, HasSizeT, HasAlignValT, HasTypeIdentity;
+    bool Destroying;
+    SizedDeallocation SizedDelete;
+    AlignedAllocation AlignedDelete;
+    TypeAwareAllocation TypeAwareDelete;
     SemaCUDA::CUDAFunctionPreference CUDAPref;
   };
 }
@@ -1978,7 +1989,8 @@ static UsualDeallocFnInfo resolveDeallocationOverload(
 /// we need to store the array size (even if the type is
 /// trivially-destructible).
 static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
-                                         bool PassType, QualType allocType) {
+                                         TypeAwareAllocation PassType,
+                                         QualType allocType) {
   const RecordType *record =
     allocType->getBaseElementTypeUnsafe()->getAs<RecordType>();
   if (!record) return false;
@@ -2004,11 +2016,10 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
   //   If the deallocation functions have class scope, the one without a
   //   parameter of type std::size_t is selected.
   ImplicitDeallocationParameters IDP = {
-      .PassTypeIdentity = PassType,
-      .PassAlignment = hasNewExtendedAlignment(S, allocType),
-      .PassSize = false};
+      PassType, alignedAllocation(hasNewExtendedAlignment(S, allocType)),
+      SizedDeallocation::No};
   auto Best = resolveDeallocationOverload(S, ops, IDP, allocType);
-  return Best && Best.HasSizeT;
+  return Best && Best.isSized();
 }
 
 ExprResult
@@ -2421,9 +2432,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
       AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
   unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
   ImplicitAllocationParameters IAP = {
-      .PassTypeIdentity = AllowTypeAwareAllocators(),
-      .PassAlignment =
-          getLangOpts().AlignedAllocation && Alignment > NewAlignment};
+      typeAwareAllocation(AllowTypeAwareAllocators()),
+      alignedAllocation(getLangOpts().AlignedAllocation &&
+                        Alignment > NewAlignment)};
 
   if (CheckArgsForPlaceholders(PlacementArgs))
     return ExprError();
@@ -2455,11 +2466,11 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     // argument. Skip the second parameter too if we're passing in the
     // alignment; we've already filled it in.
     unsigned NumImplicitArgs = 1;
-    if (IAP.PassTypeIdentity) {
+    if (IAP.passTypeIdentity()) {
       assert(OperatorNew->isTypeAwareOperatorNewOrDelete());
       NumImplicitArgs++;
     }
-    if (IAP.PassAlignment)
+    if (IAP.passAlignment())
       NumImplicitArgs++;
     if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto,
                                NumImplicitArgs, PlacementArgs, AllPlaceArgs,
@@ -2525,7 +2536,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     CallArgs.emplace_back(AllocationSize
                               ? static_cast<Expr *>(&AllocationSizeLiteral)
                               : &OpaqueAllocationSize);
-    if (IAP.PassAlignment)
+    if (IAP.passAlignment())
       CallArgs.emplace_back(&DesiredAlignment);
     CallArgs.insert(CallArgs.end(), PlacementArgs.begin(), PlacementArgs.end());
 
@@ -2536,7 +2547,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() && !IAP.PassAlignment &&
+    if (PlacementArgs.empty() && !IAP.passAlignment() &&
         (OperatorNew->isImplicit() ||
          (OperatorNew->getBeginLoc().isValid() &&
           getSourceManager().isInSystemHeader(OperatorNew->getBeginLoc())))) {
@@ -2669,8 +2680,9 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
 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) {
+    SmallVectorImpl<Expr *> &Args, AlignedAllocation &PassAlignment,
+    FunctionDecl *&Operator, OverloadCandidateSet *AlignedCandidates,
+    Expr *AlignArg, bool Diagnose) {
   unsigned NonTypeArgumentOffset = 0;
   if (Mode == ResolveMode::Typed)
     ++NonTypeArgumentOffset;
@@ -2717,8 +2729,8 @@ static bool resolveAllocationOverloadInterior(
     //   If no matching function is found and the allocated object type has
     //   new-extended alignment, the alignment argument is removed from the
     //   argument list, and overload resolution is performed again.
-    if (PassAlignment) {
-      PassAlignment = false;
+    if (PassAlignment == AlignedAllocation::Yes) {
+      PassAlignment = AlignedAllocation::No;
       AlignArg = Args[NonTypeArgumentOffset + 1];
       Args.erase(Args.begin() + NonTypeArgumentOffset + 1);
       return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
@@ -2851,12 +2863,12 @@ static bool resolveAllocationOverload(
     ImplicitAllocationParameters &IAP, FunctionDecl *&Operator,
     OverloadCandidateSet *AlignedCandidates, Expr *AlignArg, bool Diagnose) {
   Operator = nullptr;
-  if (IAP.PassTypeIdentity) {
+  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;
+    AlignedAllocation InitialAlignmentMode = IAP.PassAlignment;
     if (resolveAllocationOverloadInterior(
             S, R, Range, ResolveMode::Typed, Args, IAP.PassAlignment, Operator,
             AlignedCandidates, AlignArg, Diagnose))
@@ -2864,7 +2876,7 @@ static bool resolveAllocationOverload(
     if (Operator)
       return false;
     // There's no type aware allocator
-    IAP.PassTypeIdentity = false;
+    IAP.PassTypeIdentity = TypeAwareAllocation::No;
     // Restore alignment requirements
     IAP.PassAlignment = InitialAlignmentMode;
     // Finally prepare the type free parameter list
@@ -2893,7 +2905,7 @@ bool Sema::FindAllocationFunctions(
   //   placement form.
 
   SmallVector<Expr*, 8> AllocArgs;
-  unsigned ImplicitArgCount = 1 + IAP.PassAlignment + IAP.PassTypeIdentity;
+  unsigned ImplicitArgCount = 1 + IAP.passAlignment() + IAP.passTypeIdentity();
   AllocArgs.reserve(ImplicitArgCount + PlaceArgs.size());
 
   // C++ [expr.new]p8:
@@ -2915,18 +2927,18 @@ bool Sema::FindAllocationFunctions(
   // 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 (IAP.passTypeIdentity()) {
     if (std::optional<QualType> SpecializedTypeIdentity =
             instantiateSpecializedTypeIdentity(AllocElemType)) {
       TypeIdentity = *SpecializedTypeIdentity;
     } else {
-      IAP.PassTypeIdentity = false;
+      IAP.PassTypeIdentity = TypeAwareAllocation::No;
     }
   }
-  bool OriginalTypeAwareState = IAP.PassTypeIdentity;
+  TypeAwareAllocation OriginalTypeAwareState = IAP.PassTypeIdentity;
 
   CXXScalarValueInitExpr TypeIdentityParam(TypeIdentity, nullptr, StartLoc);
-  if (IAP.PassTypeIdentity)
+  if (IAP.passTypeIdentity())
     AllocArgs.push_back(&TypeIdentityParam);
 
   QualType SizeTy = Context.getSizeType();
@@ -2936,12 +2948,12 @@ bool Sema::FindAllocationFunctions(
   AllocArgs.push_back(&Size);
 
   QualType AlignValT = Context.VoidTy;
-  if (IAP.PassAlignment) {
+  if (IAP.passAlignment()) {
     DeclareGlobalNewDelete();
     AlignValT = Context.getTypeDeclType(getStdAlignValT());
   }
   CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
-  if (IAP.PassAlignment)
+  if (IAP.passAlignment())
     AllocArgs.push_back(&Align);
 
   AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end());
@@ -3047,9 +3059,10 @@ bool Sema::FindAllocationFunctions(
       return true;
 
     DeclareGlobalNewDelete();
-    DeallocLookupMode LookupMode = OriginalTypeAwareState
-                                       ? DeallocLookupMode::OptionallyTyped
-                                       : DeallocLookupMode::Untyped;
+    DeallocLookupMode LookupMode =
+        OriginalTypeAwareState == TypeAwareAllocation::Yes
+            ? DeallocLookupMode::OptionallyTyped
+            : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                       DeleteName, AllocElemType);
   }
@@ -3073,7 +3086,7 @@ bool Sema::FindAllocationFunctions(
   // type uses the sized or non-sized form of aligned operator delete.
 
   unsigned NonPlacementNewArgCount = 1; // size parameter
-  if (IAP.PassTypeIdentity)
+  if (IAP.passTypeIdentity())
     ++NonPlacementNewArgCount;
   bool isPlacementNew = !PlaceArgs.empty() ||
                         OperatorNew->param_size() != NonPlacementNewArgCount ||
@@ -3095,7 +3108,7 @@ bool Sema::FindAllocationFunctions(
       auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
 
       SmallVector<QualType, 4> ArgTypes;
-      if (IAP.PassTypeIdentity)
+      if (IAP.passTypeIdentity())
         ArgTypes.push_back(TypeIdentity);
       ArgTypes.push_back(Context.VoidPtrTy);
       for (unsigned I = ArgTypes.size(), N = Proto->getNumParams(); I < N; ++I)
@@ -3144,9 +3157,9 @@ bool Sema::FindAllocationFunctions(
     // 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};
+        OriginalTypeAwareState,
+        alignedAllocation(hasNewExtendedAlignment(*this, AllocElemType)),
+        FoundGlobalDelete ? SizedDeallocation::Yes : SizedDeallocation::No};
     UsualDeallocFnInfo Selected = resolveDeallocationOverload(
         *this, FoundDelete, IDP, AllocElemType, &BestDeallocFns);
     if (Selected && BestDeallocFns.empty())
@@ -3166,7 +3179,7 @@ bool Sema::FindAllocationFunctions(
   if (Matches.size() == 1) {
     OperatorDelete = Matches[0].second;
     if (isTypeAwareOperatorNewOrDelete(OperatorDelete) !=
-        IAP.PassTypeIdentity) {
+        IAP.passTypeIdentity()) {
       Diag(StartLoc, diag::warn_mismatching_type_aware_cleanup_deallocator);
       int NewDiagIndex = isTypeAwareOperatorNewOrDelete(OperatorNew) ? 0 : 1;
       int DeleteDiagIndex =
@@ -3177,7 +3190,7 @@ bool Sema::FindAllocationFunctions(
            diag::note_type_aware_operator_declared)
           << DeleteDiagIndex << OperatorDelete;
     }
-    if (IAP.PassTypeIdentity &&
+    if (IAP.passTypeIdentity() &&
         OperatorDelete->getDeclContext() != OperatorNew->getDeclContext()) {
       Diag(StartLoc,
            diag::err_no_matching_type_aware_cleanup_deallocator_mismatch)
@@ -3203,16 +3216,14 @@ bool Sema::FindAllocationFunctions(
       //   If this is a member operator delete, and there is a corresponding
       //   non-sized member operator delete, this isn't /really/ a sized
       //   deallocation function, it just happens to have a size_t parameter.
-      bool IsSizedDelete = Info.HasSizeT;
+      bool IsSizedDelete = Info.SizedDelete == SizedDeallocation::Yes;
       if (IsSizedDelete && !FoundGlobalDelete) {
         ImplicitDeallocationParameters SizeTestingIDP = {
-            .PassTypeIdentity = Info.HasTypeIdentity,
-            .PassAlignment = Info.HasAlignValT,
-            .PassSize = false};
+            Info.TypeAwareDelete, Info.AlignedDelete, SizedDeallocation::No};
         auto NonSizedDelete = resolveDeallocationOverload(
             *this, FoundDelete, SizeTestingIDP, AllocElemType);
-        if (NonSizedDelete && !NonSizedDelete.HasSizeT &&
-            NonSizedDelete.HasAlignValT == Info.HasAlignValT)
+        if (NonSizedDelete && !NonSizedDelete.isSized() &&
+            NonSizedDelete.AlignedDelete == Info.AlignedDelete)
           IsSizedDelete = false;
       }
 
@@ -3528,10 +3539,9 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
 
   FunctionDecl *OperatorDelete = nullptr;
   QualType DeallocType = Context.getRecordType(RD);
-  ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
-                                            AllowTypeAwareAllocators(),
-                                        .PassAlignment = false,
-                                        .PassSize = false};
+  ImplicitDeallocationParameters IDP = {
+      typeAwareAllocation(AllowTypeAwareAllocators()), AlignedAllocation::No,
+      SizedDeallocation::No};
 
   if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, DeallocType, IDP))
     return nullptr;
@@ -3542,8 +3552,9 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
   // If there's no class-specific operator delete, look up the global
   // non-array delete.
   QualType RecordType = Context.getRecordType(RD);
-  IDP.PassAlignment = hasNewExtendedAlignment(*this, RecordType);
-  IDP.PassSize = true;
+  IDP.PassAlignment =
+      alignedAllocation(hasNewExtendedAlignment(*this, RecordType));
+  IDP.PassSize = SizedDeallocation::Yes;
   return FindUsualDeallocationFunction(RecordType, Loc, IDP, Name);
 }
 
@@ -3562,8 +3573,9 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
 
   Found.suppressDiagnostics();
 
-  IDP.PassAlignment |=
-      hasNewExtendedAlignment(*this, Context.getRecordType(RD));
+  if (!IDP.passAlignment() &&
+      hasNewExtendedAlignment(*this, Context.getRecordType(RD)))
+    IDP.PassAlignment = AlignedAllocation::Yes;
 
   // C++17 [expr.delete]p10:
   //   If the deallocation functions have class scope, the one without a
@@ -3999,10 +4011,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
                                       ArrayForm ? OO_Array_Delete : OO_Delete);
 
     if (PointeeRD) {
-      ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
-                                                AllowTypeAwareAllocators(),
-                                            .PassAlignment = false,
-                                            .PassSize = false};
+      ImplicitDeallocationParameters IDP = {
+          typeAwareAllocation(AllowTypeAwareAllocators()),
+          AlignedAllocation::No, SizedDeallocation::No};
       if (!UseGlobal &&
           FindDeallocationFunction(StartLoc, PointeeRD, DeleteName,
                                    OperatorDelete, Pointee, IDP))
@@ -4024,7 +4035,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
               UsualDeallocFnInfo(
                   *this, DeclAccessPair::make(OperatorDelete, AS_public),
                   Pointee)
-                  .HasSizeT;
+                  .isSized();
       }
 
       if (!PointeeRD->hasIrrelevantDestructor())
@@ -4054,10 +4065,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
       bool Overaligned = hasNewExtendedAlignment(*this, Pointee);
 
       // Look for a global declaration.
-      ImplicitDeallocationParameters IDP = {.PassTypeIdentity =
-                                                AllowTypeAwareAllocators(),
-                                            .PassAlignment = Overaligned,
-                                            .PassSize = CanProvideSize};
+      ImplicitDeallocationParameters IDP = {
+          typeAwareAllocation(AllowTypeAwareAllocators()),
+          alignedAllocation(Overaligned), sizedDeallocation(CanProvideSize)};
       OperatorDelete =
           FindUsualDeallocationFunction(Pointee, StartLoc, IDP, DeleteName);
       if (!OperatorDelete)

>From 63cd2d61ee8031125279a2add8477c90390a0aff Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sun, 10 Nov 2024 15:23:50 -0800
Subject: [PATCH 11/18] Renaming this function to be more clear that it is not
 just a lang opt

---
 clang/include/clang/Sema/Sema.h  |  2 +-
 clang/lib/Sema/SemaCoroutine.cpp |  6 +++---
 clang/lib/Sema/SemaDeclCXX.cpp   |  6 +++---
 clang/lib/Sema/SemaExprCXX.cpp   | 14 +++++++-------
 4 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bd66a25a0a01f2..0db395c6321465 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2135,7 +2135,7 @@ class Sema final : public SemaBase {
            isConstantEvaluatedOverride;
   }
 
-  bool AllowTypeAwareAllocators() const {
+  bool AllowTypeAwareAllocatorsInCurrentContext() const {
     return getLangOpts().TypeAwareAllocators && !isConstantEvaluatedContext();
   }
 
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index ec3a0e7a941227..b97910515481ab 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1113,7 +1113,7 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
   // scope of the promise type. If nothing is found, a search is performed in
   // the global scope.
   ImplicitDeallocationParameters IDP = {
-      typeAwareAllocation(S.AllowTypeAwareAllocators()),
+      typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
       alignedAllocation(Overaligned), SizedDeallocation::Yes};
   if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
                                  PromiseType, IDP, /*Diagnose*/ true))
@@ -1426,7 +1426,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper function to indicate whether the last lookup found the aligned
   // allocation function.
   ImplicitAllocationParameters IAP = {
-      typeAwareAllocation(S.AllowTypeAwareAllocators()),
+      typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
       alignedAllocation(S.getLangOpts().CoroAlignedAllocation)};
   auto LookupAllocationFunction =
       [&](Sema::AllocationFunctionScope NewScope = Sema::AFS_Both,
@@ -1442,7 +1442,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
         if (NewScope == Sema::AFS_Both)
           NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
 
-        IAP = {typeAwareAllocation(S.AllowTypeAwareAllocators()),
+        IAP = {typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
                alignedAllocation(!ForceNonAligned &&
                                  S.getLangOpts().CoroAlignedAllocation)};
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index d419618a57d658..aaadcdf239601a 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9715,7 +9715,7 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
     ImplicitDeallocationParameters IDP = {
-        typeAwareAllocation(AllowTypeAwareAllocators()), AlignedAllocation::No,
+        typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()), AlignedAllocation::No,
         SizedDeallocation::No};
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
                                  OperatorDelete, DeallocType, IDP,
@@ -16159,7 +16159,7 @@ bool Sema::isTypeAwareOperatorNewOrDelete(const NamedDecl *ND) const {
 std::optional<FunctionDecl *>
 Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
                                       QualType DeallocType) {
-  if (!AllowTypeAwareAllocators())
+  if (!AllowTypeAwareAllocatorsInCurrentContext())
     return std::nullopt;
 
   TemplateParameterList *TemplateParameters =
@@ -16234,7 +16234,7 @@ Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
 
 std::optional<QualType>
 Sema::instantiateSpecializedTypeIdentity(QualType Subject) {
-  assert(AllowTypeAwareAllocators());
+  assert(AllowTypeAwareAllocatorsInCurrentContext());
   ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
   if (!TypeIdentity)
     return std::nullopt;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d10b42ea07755a..869056728f2721 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1754,7 +1754,7 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
     return false;
 
   unsigned UsualParams = 1;
-  if (S.AllowTypeAwareAllocators() && UsualParams < FD->getNumParams() &&
+  if (S.AllowTypeAwareAllocatorsInCurrentContext() && UsualParams < FD->getNumParams() &&
       S.isTypeAwareOperatorNewOrDelete(FD))
     ++UsualParams;
 
@@ -1795,7 +1795,7 @@ namespace {
         FD = *InstantiatedDecl;
       }
       unsigned NumBaseParams = 1;
-      if (S.AllowTypeAwareAllocators() &&
+      if (S.AllowTypeAwareAllocatorsInCurrentContext() &&
           S.isTypeAwareOperatorNewOrDelete(FD)) {
         QualType TypeIdentityTag = FD->getParamDecl(0)->getType();
         std::optional<QualType> ExpectedTypeIdentityTag =
@@ -2432,7 +2432,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
       AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
   unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
   ImplicitAllocationParameters IAP = {
-      typeAwareAllocation(AllowTypeAwareAllocators()),
+      typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
       alignedAllocation(getLangOpts().AlignedAllocation &&
                         Alignment > NewAlignment)};
 
@@ -3509,7 +3509,7 @@ FunctionDecl *Sema::FindUsualDeallocationFunction(
   DeclareGlobalNewDelete();
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
-  DeallocLookupMode LookupMode = AllowTypeAwareAllocators()
+  DeallocLookupMode LookupMode = AllowTypeAwareAllocatorsInCurrentContext()
                                      ? DeallocLookupMode::OptionallyTyped
                                      : DeallocLookupMode::Untyped;
   LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
@@ -3540,7 +3540,7 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
   FunctionDecl *OperatorDelete = nullptr;
   QualType DeallocType = Context.getRecordType(RD);
   ImplicitDeallocationParameters IDP = {
-      typeAwareAllocation(AllowTypeAwareAllocators()), AlignedAllocation::No,
+      typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()), AlignedAllocation::No,
       SizedDeallocation::No};
 
   if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, DeallocType, IDP))
@@ -4012,7 +4012,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
 
     if (PointeeRD) {
       ImplicitDeallocationParameters IDP = {
-          typeAwareAllocation(AllowTypeAwareAllocators()),
+          typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
           AlignedAllocation::No, SizedDeallocation::No};
       if (!UseGlobal &&
           FindDeallocationFunction(StartLoc, PointeeRD, DeleteName,
@@ -4066,7 +4066,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
 
       // Look for a global declaration.
       ImplicitDeallocationParameters IDP = {
-          typeAwareAllocation(AllowTypeAwareAllocators()),
+          typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
           alignedAllocation(Overaligned), sizedDeallocation(CanProvideSize)};
       OperatorDelete =
           FindUsualDeallocationFunction(Pointee, StartLoc, IDP, DeleteName);

>From cc10778dc19c5b631426d012c2558afc0140e9e5 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 11 Nov 2024 14:15:00 -0800
Subject: [PATCH 12/18] Add a test for qualifiers out of a sense of paranoia

---
 .../type-aware-new-delete-qualifiers.cpp      | 129 ++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp

diff --git a/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp b/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
new file mode 100644
index 00000000000000..9a90e044fe9c5c
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++17    -fexperimental-cxx-type-aware-allocators
+
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+using size_t = __SIZE_TYPE__;
+
+
+template <class Tp> struct is_const {
+  static const bool value = false;
+};
+template <class Tp> struct is_const<Tp const> {
+  static const bool value = true;
+};
+
+template <class Tp> struct is_volatile {
+  static const bool value = false;
+};
+template <class Tp> struct is_volatile<Tp volatile> {
+  static const bool value = true;
+};
+
+template <class T> static const bool is_const_v = is_const<T>::value;
+template <class T> static const bool is_volatile_v = is_volatile<T>::value;
+
+template <bool expectConst, bool expectVolatile>
+struct VerifyQualifiers {
+  template <typename T> void *operator new(std::type_identity<T>, size_t) throw() {
+    static_assert(is_const_v<T> == expectConst); // #1
+    static_assert(is_volatile_v<T> == expectVolatile); // #2
+    return 0;
+  }
+  template <typename T> void operator delete(std::type_identity<T>, void*) {
+    static_assert(is_const_v<T> == expectConst); // #3
+    static_assert(is_volatile_v<T> == expectVolatile); // #4
+  }
+};
+
+template <bool expectConst, bool expectVolatile> void *operator new(std::type_identity<VerifyQualifiers<expectConst, expectVolatile> > type, size_t) throw() {
+  static_assert(is_const_v<typename decltype(type)::type> == expectConst); // #5
+  static_assert(is_volatile_v<typename decltype(type)::type> == expectVolatile); // #6
+  return 0;
+}
+
+template <bool expectConst, bool expectVolatile> void operator delete(std::type_identity<VerifyQualifiers<expectConst, expectVolatile> > type, void*) {
+  static_assert(is_const_v<typename decltype(type)::type> == expectConst); // #7
+  static_assert(is_volatile_v<typename decltype(type)::type> == expectVolatile); // #8
+}
+
+// Success tests
+void test_member_allocators() {
+  auto *unqualified_obj = new VerifyQualifiers<false, false>();
+  delete unqualified_obj;
+  auto *const_obj = new const VerifyQualifiers<true, false>();
+  delete const_obj;
+  auto *volatile_obj = new volatile VerifyQualifiers<false, true>();
+  delete volatile_obj;
+  auto *const_volatile_obj = new const volatile VerifyQualifiers<true, true>();
+  delete const_volatile_obj;
+}
+
+void test_global_allocators() {
+  auto *unqualified_obj = ::new VerifyQualifiers<false, false>();
+  ::delete unqualified_obj;
+  auto *const_obj = ::new const VerifyQualifiers<true, false>();
+  ::delete const_obj;
+  auto *volatile_obj = ::new volatile VerifyQualifiers<false, true>();
+  ::delete volatile_obj;
+  auto *const_volatile_obj = ::new const volatile VerifyQualifiers<true, true>();
+  ::delete const_volatile_obj;
+}
+
+// Verify mismatches
+void test_incorrect_member_allocators() {
+  VerifyQualifiers<true, false> *incorrect_const_obj = new VerifyQualifiers<true, false>();
+  // expected-error@#1 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, false>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'VerifyQualifiers<true, false>::operator new<VerifyQualifiers<true, false>>' requested here}}
+  delete incorrect_const_obj;
+  // expected-error@#3 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, false>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'VerifyQualifiers<true, false>::operator delete<VerifyQualifiers<true, false>>' requested here}}
+
+  VerifyQualifiers<false, true> *incorrect_volatile_obj = new VerifyQualifiers<false, true>();
+  // expected-error@#2 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<false, true>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'VerifyQualifiers<false, true>::operator new<VerifyQualifiers<false, true>>' requested here}}
+  delete incorrect_volatile_obj;
+  // expected-error@#4 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<false, true>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'VerifyQualifiers<false, true>::operator delete<VerifyQualifiers<false, true>>' requested here}}
+
+  VerifyQualifiers<true, true> *incorrect_const_volatile_obj = new VerifyQualifiers<true, true>();
+  // expected-error@#1 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-error@#2 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-note at -3 {{in instantiation of function template specialization 'VerifyQualifiers<true, true>::operator new<VerifyQualifiers<true, true>>' requested here}}
+  delete incorrect_const_volatile_obj;
+  // expected-error@#3 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-error@#4 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-note at -3 {{in instantiation of function template specialization 'VerifyQualifiers<true, true>::operator delete<VerifyQualifiers<true, true>>' requested here}}
+}
+
+
+// Verify mismatches
+void test_incorrect_global_allocators() {
+  VerifyQualifiers<true, false> *incorrect_const_obj = ::new VerifyQualifiers<true, false>();
+  // expected-error@#5 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, false>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'operator new<true, false>' requested here}}
+  ::delete incorrect_const_obj;
+  // expected-error@#7 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, false>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'operator delete<true, false>' requested here}}
+
+  VerifyQualifiers<false, true> *incorrect_volatile_obj = ::new VerifyQualifiers<false, true>();
+  // expected-error@#6 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<false, true>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'operator new<false, true>' requested here}}
+  ::delete incorrect_volatile_obj;
+  // expected-error@#8 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<false, true>> == true'}}
+  // expected-note at -2 {{in instantiation of function template specialization 'operator delete<false, true>' requested here}}
+
+  VerifyQualifiers<true, true> *incorrect_const_volatile_obj = ::new VerifyQualifiers<true, true>();
+  // expected-error@#5 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-error@#6 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-note at -3 {{in instantiation of function template specialization 'operator new<true, true>' requested here}}
+  ::delete incorrect_const_volatile_obj;
+  // expected-error@#7 {{static assertion failed due to requirement 'is_const_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-error@#8 {{static assertion failed due to requirement 'is_volatile_v<VerifyQualifiers<true, true>> == true'}}
+  // expected-note at -3 {{in instantiation of function template specialization 'operator delete<true, true>' requested here}}
+}

>From ba961a4cfa2b156084490abc41d07609b5397a2a Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 11 Nov 2024 17:45:15 -0800
Subject: [PATCH 13/18] Add an explicit test to ensure type aware delete is
 called for cleanup

Annoyingly this needs to be a codegen test for sema. The long term fix is
probably to make new expressions actually build up a full AST node of
dependent expressions, rather than including a bunch of flags to tell
codegen what to do.
---
 ...ype-aware-usual-deallocation-functions.cpp | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp

diff --git a/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp b/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp
new file mode 100644
index 00000000000000..23c8a0161f7f92
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 %s -o - -emit-llvm -std=c++23 -faligned-allocation -fexperimental-cxx-type-aware-allocators -fexceptions | FileCheck %s
+// RUN: %clang_cc1 %s -o - -emit-llvm -std=c++23 -faligned-allocation                                          -fexceptions | FileCheck %s
+
+// This is a semantic test, but the only way to observe the pickup of the usual delete
+// for a constructor call's clean up
+
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : __SIZE_TYPE__ {};
+}
+
+using size_t = __SIZE_TYPE__;
+
+
+struct __attribute__((aligned(128))) S1 {
+    S1();
+#if __has_feature(cxx_type_aware_allocators)
+    template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, U) asm("S1_operator_new");
+    template <typename T, typename U> void operator delete(std::type_identity<T>, void*, U) asm("S1_cleanup_operator_delete");
+#else
+    template <typename U> void *operator new(size_t, U) asm("S1_operator_new");
+    template <typename U> void operator delete(void*, U) asm("S1_cleanup_operator_delete");
+#endif
+};
+struct __attribute__((aligned(128))) S2 {
+    S2();
+#if __has_feature(cxx_type_aware_allocators)
+    template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) asm("S2_operator_new");
+    template <typename T, typename U> void operator delete(std::type_identity<T>, void*, U) asm("S2_cleanup_operator_delete");
+#else
+    void *operator new(size_t, std::align_val_t) asm("S2_operator_new");
+    template <typename U> void operator delete(void*, U) asm("S2_cleanup_operator_delete");
+#endif
+};
+
+struct __attribute__((aligned(128))) S3 {
+    S3();
+#if __has_feature(cxx_type_aware_allocators)
+    template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, U) asm("S3_operator_new");
+    template <typename T> void operator delete(std::type_identity<T>, void*, std::align_val_t) asm("S3_cleanup_operator_delete");
+#else
+    template <typename U> void *operator new(size_t, U) asm("S3_operator_new");
+    void operator delete(void*, std::align_val_t) asm("S3_cleanup_operator_delete");
+#endif
+};
+
+extern "C" void test1() {
+    S1 *s = new S1;
+// CHECK-LABEL: test1
+// CHECK: S1_operator_new
+// CHECK: S1_cleanup_operator_delete
+}
+
+extern "C" void test2() {
+    S2 *s = new S2;
+// CHECK-LABEL: test2
+// CHECK: S2_operator_new
+// CHECK: S2_cleanup_operator_delete
+}
+
+extern "C" void test3() {
+    S3 *s = new S3;
+// CHECK-LABEL: test3
+// CHECK: S3_operator_new
+// CHECK: S3_cleanup_operator_delete
+}
+

>From 198f92ecae49273401309fde2defb7c8bf41c173 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 11 Nov 2024 22:11:33 -0800
Subject: [PATCH 14/18] More paranoia tests

---
 ...ype-aware-usual-deallocation-functions.cpp | 39 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp b/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp
index 23c8a0161f7f92..479ecdafe95621 100644
--- a/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp
+++ b/clang/test/SemaCXX/type-aware-usual-deallocation-functions.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -o - -emit-llvm -std=c++23 -faligned-allocation -fexperimental-cxx-type-aware-allocators -fexceptions | FileCheck %s
+// RUN: %clang_cc1 %s -o - -emit-llvm -std=c++23 -faligned-allocation -fexperimental-cxx-type-aware-allocators -fexceptions | FileCheck --check-prefixes=CHECK,CHECK_TA %s
 // RUN: %clang_cc1 %s -o - -emit-llvm -std=c++23 -faligned-allocation                                          -fexceptions | FileCheck %s
 
 // This is a semantic test, but the only way to observe the pickup of the usual delete
@@ -46,6 +46,28 @@ struct __attribute__((aligned(128))) S3 {
 #endif
 };
 
+#if __has_feature(cxx_type_aware_allocators)
+struct __attribute__((aligned(128))) S4 {
+    S4();
+};
+
+template <typename U> void *operator new(std::type_identity<S4>, size_t, U) asm("S4_operator_new");
+template <typename U> void operator delete(std::type_identity<S4>, void*, U) asm("S4_cleanup_operator_delete");
+#endif
+
+template <typename AlignValT>
+struct __attribute__((aligned(128))) S5 {
+    S5();
+#if __has_feature(cxx_type_aware_allocators)
+    template <typename T> void *operator new(std::type_identity<T>, size_t, AlignValT) asm("S5_operator_new");
+    template <typename T> void operator delete(std::type_identity<T>, void*, AlignValT) asm("S5_cleanup_operator_delete");
+#else
+    void *operator new(size_t, AlignValT) asm("S5_operator_new");
+    void operator delete(void*, AlignValT) asm("S5_cleanup_operator_delete");
+#endif
+};
+
+
 extern "C" void test1() {
     S1 *s = new S1;
 // CHECK-LABEL: test1
@@ -67,3 +89,18 @@ extern "C" void test3() {
 // CHECK: S3_cleanup_operator_delete
 }
 
+#if __has_feature(cxx_type_aware_allocators)
+extern "C" void test4() {
+    S4 *s = new S4;
+// CHECK_TA-LABEL: test4
+// CHECK_TA: S4_operator_new
+// CHECK_TA: S4_cleanup_operator_delete
+}
+#endif
+
+extern "C" void test5() {
+    S5<std::align_val_t> *s = new S5<std::align_val_t>;
+// CHECK-LABEL: test5
+// CHECK: S5_operator_new
+// CHECK: S5_cleanup_operator_delete
+}

>From fe95e78d3d43ca12b78509182c05a97b62470f70 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 11 Nov 2024 22:14:29 -0800
Subject: [PATCH 15/18] Reformat following the grand enum refactoring

---
 clang/lib/Sema/SemaCoroutine.cpp | 55 ++++++++++++++++----------------
 clang/lib/Sema/SemaDeclCXX.cpp   |  4 +--
 clang/lib/Sema/SemaExprCXX.cpp   |  8 ++---
 3 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index b97910515481ab..278e30a870b66d 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1428,33 +1428,34 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   ImplicitAllocationParameters IAP = {
       typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
       alignedAllocation(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 or via ADL for typed
-        // allocation.
-        if (NewScope == Sema::AFS_Both)
-          NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
-
-        IAP = {typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
-               alignedAllocation(!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);
-      };
+  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 = {typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
+           alignedAllocation(!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
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index aaadcdf239601a..06ff6f15d7cb9c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9715,8 +9715,8 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
     ImplicitDeallocationParameters IDP = {
-        typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()), AlignedAllocation::No,
-        SizedDeallocation::No};
+        typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
+        AlignedAllocation::No, SizedDeallocation::No};
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
                                  OperatorDelete, DeallocType, IDP,
                                  /*Diagnose*/ false)) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 869056728f2721..57a32a7bc5753e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1754,8 +1754,8 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
     return false;
 
   unsigned UsualParams = 1;
-  if (S.AllowTypeAwareAllocatorsInCurrentContext() && UsualParams < FD->getNumParams() &&
-      S.isTypeAwareOperatorNewOrDelete(FD))
+  if (S.AllowTypeAwareAllocatorsInCurrentContext() &&
+      UsualParams < FD->getNumParams() && S.isTypeAwareOperatorNewOrDelete(FD))
     ++UsualParams;
 
   if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() &&
@@ -3540,8 +3540,8 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
   FunctionDecl *OperatorDelete = nullptr;
   QualType DeallocType = Context.getRecordType(RD);
   ImplicitDeallocationParameters IDP = {
-      typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()), AlignedAllocation::No,
-      SizedDeallocation::No};
+      typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
+      AlignedAllocation::No, SizedDeallocation::No};
 
   if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, DeallocType, IDP))
     return nullptr;

>From 5e873ef408b89bd1803637674276607060ebfd9d Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Tue, 12 Nov 2024 17:46:47 -0800
Subject: [PATCH 16/18] Update error messages, and subsequently the tests

---
 .../clang/Basic/DiagnosticSemaKinds.td        | 11 ++--
 clang/lib/Sema/SemaDeclCXX.cpp                | 28 +++++----
 .../test/SemaCXX/type-aware-new-constexpr.cpp |  2 +-
 .../SemaCXX/type-aware-new-delete-arrays.cpp  |  2 +-
 ...are-new-delete-basic-free-declarations.cpp | 32 ++++++++--
 ...new-delete-basic-in-class-declarations.cpp | 59 +++++++++++++++++--
 ...type-aware-new-delete-basic-resolution.cpp |  4 +-
 .../type-aware-new-delete-qualifiers.cpp      |  2 +-
 8 files changed, 107 insertions(+), 33 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5c631846042514..3cc728e47e08b3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9694,16 +9694,17 @@ def warn_operator_new_returns_null : Warning<
   "%select{| or 'noexcept'}1">, InGroup<OperatorNewReturnsNull>;
 
 def err_operator_new_dependent_param_type : Error<
-  "%0 cannot take a dependent type as first parameter; "
-  "use size_t (%1) instead">;
+  "%select{|type aware}1 %0 cannot take a dependent type as %select{first|second}1 parameter; "
+  "use size_t (%2) instead">;
 def err_operator_new_param_type : Error<
-  "%0 takes type size_t (%1) as first parameter">;
+  "%select{|type aware}1 %0 takes type size_t (%2) as %select{first|second}1 parameter">;
 def err_operator_new_default_arg: Error<
   "parameter of %0 cannot have a default argument">;
 def err_operator_delete_dependent_param_type : Error<
-  "%0 cannot take a dependent type as first parameter; use %1 instead">;
+  "%select{|type aware}1 %0 cannot take a dependent type as %select{first|second}1 parameter; "
+  "use %2 instead">;
 def err_operator_delete_param_type : Error<
-  "first parameter of %0 must have type %1">;
+  "%select{first|second}1 parameter of%select{|type aware}1 %0 must have type %2">;
 def err_destroying_operator_delete_not_usual : Error<
   "destroying operator delete can have only an optional size and optional "
   "alignment parameter">;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 06ff6f15d7cb9c..f6c4fc136a005d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16291,6 +16291,16 @@ static inline bool CheckOperatorNewDeleteTypes(
     }
     return SemaRef.Context.getCanonicalType(T);
   };
+
+  unsigned FirstNonTypeParam = 0;
+  if (FnDecl->isTypeAwareOperatorNewOrDelete()) {
+    if (!SemaRef.getLangOpts().TypeAwareAllocators) {
+      return SemaRef.Diag(FnDecl->getLocation(),
+                          diag::err_unsupported_type_aware_allocator);
+    }
+    ++FirstNonTypeParam;
+  }
+
   auto *FnType = FnDecl->getType()->castAs<FunctionType>();
   QualType CanResultType = NormalizeType(FnType->getReturnType());
   QualType CanExpectedResultType = NormalizeType(ExpectedResultType);
@@ -16314,15 +16324,6 @@ static inline bool CheckOperatorNewDeleteTypes(
                       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() <= FirstNonTypeParam)
     return SemaRef.Diag(FnDecl->getLocation(),
@@ -16340,7 +16341,8 @@ static inline bool CheckOperatorNewDeleteTypes(
     return SemaRef.Diag(FnDecl->getLocation(), FirstParamType->isDependentType()
                                                    ? DependentParamTypeDiag
                                                    : InvalidParamTypeDiag)
-           << FnDecl->getDeclName() << ExpectedFirstParamType;
+           << FnDecl->getDeclName() << FirstNonTypeParam
+           << ExpectedFirstParamType;
   }
 
   *MinimumNonDefaultArgs = FirstNonTypeParam + 1;
@@ -16368,7 +16370,7 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
           diag::err_operator_new_dependent_param_type,
           diag::err_operator_new_param_type, &MinimumNonDefaultArgs))
     return true;
-  assert(MinimumNonDefaultArgs > 0);
+  assert(MinimumNonDefaultArgs > 0 && MinimumNonDefaultArgs <= 2);
   // C++ [basic.stc.dynamic.allocation]p1:
   //  The first parameter shall not have an associated default argument.
   for (unsigned Idx = 0; Idx < MinimumNonDefaultArgs; ++Idx) {
@@ -16376,7 +16378,7 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
     if (ParamDecl->hasDefaultArg()) {
       return SemaRef.Diag(FnDecl->getLocation(),
                           diag::err_operator_new_default_arg)
-             << FnDecl->getDeclName() << ParamDecl->getDefaultArgRange();
+             << FnDecl->getDeclName() << Idx << ParamDecl->getDefaultArgRange();
     }
   }
   return false;
@@ -16412,7 +16414,7 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
           diag::err_operator_delete_param_type, &MinimumNonDefaultArgs))
     return true;
 
-  assert(MinimumNonDefaultArgs > 0);
+  assert(MinimumNonDefaultArgs > 0 && MinimumNonDefaultArgs <= 2);
   // C++ P0722:
   //   A destroying operator delete shall be a usual deallocation function.
   if (MD && !MD->getParent()->isDependentContext() &&
diff --git a/clang/test/SemaCXX/type-aware-new-constexpr.cpp b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
index a8f3fa4d0d0169..0734f92c82d6a3 100644
--- a/clang/test/SemaCXX/type-aware-new-constexpr.cpp
+++ b/clang/test/SemaCXX/type-aware-new-constexpr.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions 
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++23 -fexperimental-cxx-type-aware-allocators -fexceptions
 
 namespace std {
   template <class T> struct type_identity {};
diff --git a/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp b/clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
index 2ead0fb2110404..89d8c4d8b2577d 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 -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions -Wall -Wpedantic
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++23 -fexperimental-cxx-type-aware-allocators -fexceptions -Wall -Wpedantic
 
 namespace std {
   template <class T> struct type_identity {};
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 6fe2438af01813..26eb703dfff306 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
@@ -1,5 +1,5 @@
-// 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
+// RUN: %clang_cc1 -fsyntax-only -verify %s          -std=c++23 -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TAA -std=c++23 -fno-experimental-cxx-type-aware-allocators
 
 namespace std {
   template <class T> struct type_identity {};
@@ -80,16 +80,36 @@ template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t); // #21
 #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}}
+// expected-error at -1 {{'operator new' cannot take a dependent type as first parameter}}
 
 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}}
+// expected-error at -1 {{'operator delete' cannot take a dependent type as first parameter}}
 
 template <typename T> struct S {
   typedef std::type_identity<T> type_identity;
+  typedef size_t size_ty;
+  typedef void *ptr_ty;
 };
 
 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}}
+// expected-error at -1 {{'operator new' cannot take a dependent type as first parameter}}
+
+#if !defined(NO_TAA)
+template <typename U> void *operator new(std::type_identity<int>, U);
+// expected-error at -1 {{type aware 'operator new' cannot take a dependent type as second parameter}}
+template <typename U> void operator delete(std::type_identity<int>, U);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as second parameter}}
+template <typename T, typename U> void *operator new(std::type_identity<T>, U);
+// expected-error at -1 {{type aware 'operator new' cannot take a dependent type as second parameter}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, U);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as second parameter}}
+template <typename U> void *operator new(std::type_identity<int>, typename S<U>::size_ty);
+// expected-error at -1 {{type aware 'operator new' cannot take a dependent type as second parameter}}
+template <typename U> void operator delete(std::type_identity<int>, typename S<U>::ptr_ty);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as second parameter}}
+template <typename T, typename U> void *operator new(std::type_identity<T>, typename S<U>::size_ty);
+// expected-error at -1 {{type aware 'operator new' cannot take a dependent type as second parameter}}
+template <typename T, typename U> void operator delete(std::type_identity<T>, typename S<U>::ptr_ty);
+// expected-error at -1 {{type aware 'operator delete' cannot take a dependent type as second parameter}}
+#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 d3b4264ff3e727..fedc209bc3f741 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
@@ -1,6 +1,6 @@
-// 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 -fexperimental-cxx-type-aware-destroying-delete
-// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TAA  -std=c++17 -fno-experimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++23    -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 -fsyntax-only -verify %s           -std=c++23    -fexperimental-cxx-type-aware-allocators -fexperimental-cxx-type-aware-destroying-delete
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TAA  -std=c++23 -fno-experimental-cxx-type-aware-allocators
 
 namespace std {
   template <class T> struct type_identity {};
@@ -59,6 +59,57 @@ struct S5 {
 #if defined(NO_TAA)
   // expected-error@#10 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
 #else
-  // expected-error@#10 {{'operator delete' cannot take a dependent type as first parameter; use 'void *'}}
+  // expected-error@#10 {{type aware 'operator delete' cannot take a dependent type as second parameter}}
 #endif
 };
+
+struct S6 {
+  template <typename T> void *operator new(std::type_identity<S6>, T); // #11
+#if defined(NO_TAA)
+  // expected-error@#11 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+#else
+  // expected-error@#11 {{type aware 'operator new' cannot take a dependent type as second parameter}}
+#endif
+  template <typename T> void operator delete(std::type_identity<S6>, T); // #12
+#if defined(NO_TAA)
+  // expected-error@#12 {{type aware allocation operators are disabled, enable with '-fexperimental-cxx-type-aware-allocators'}}
+#else
+  // expected-error@#12 {{type aware 'operator delete' cannot take a dependent type as second parameter}}
+#endif
+};
+
+#if !defined(NO_TAA)
+template <typename U>
+struct S7 {
+  template <typename T> void *operator new(std::type_identity<T>, U); // #13
+  // expected-error@#13 {{type aware 'operator new' cannot take a dependent type as second parameter;}}
+  template <typename T> void operator delete(std::type_identity<T>, U); // #14
+  // expected-error@#14 {{type aware 'operator delete' cannot take a dependent type as second parameter;}}
+#if !defined(NO_TADD)
+  template <typename T> void operator delete(std::type_identity<T>, S7 *, std::destroying_delete_t, U); // #15
+#endif
+  void operator delete(S7 *, std::destroying_delete_t, U); // #16
+};
+
+void f() {
+  S7<int> s;
+  // expected-note at -1 {{in instantiation of template class 'S7<int>' requested here}}
+#if !defined(NO_TADD)
+  // expected-error@#15 {{destroying operator delete can have only an optional size and optional alignment parameter}}
+#endif
+  // expected-error@#16 {{destroying operator delete can have only an optional size and optional alignment parameter}}
+}
+
+struct S8 {
+  template <typename T, typename U> void *operator new(std::type_identity<T>, U); // #17
+  // expected-error@#17 {{type aware 'operator new' cannot take a dependent type as second parameter;}}
+  template <typename T, typename U> void operator delete(std::type_identity<T>, U); // #18
+  // expected-error@#18 {{type aware 'operator delete' cannot take a dependent type as second parameter;}}
+#if !defined(NO_TADD)
+  template <typename T, typename U> void operator delete(std::type_identity<T>, S8 *, std::destroying_delete_t, U); // #19
+  // expected-error@#19 {{destroying operator delete can have only an optional size and optional alignment parameter}}
+#endif
+};
+
+
+#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 7a3c224c74a04e..b010006fb7979c 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -1,5 +1,5 @@
-// 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 -fexperimental-cxx-type-aware-destroying-delete -fexceptions
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++23 -fexperimental-cxx-type-aware-allocators -fexceptions
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DTADD -std=c++23 -fexperimental-cxx-type-aware-allocators -fexperimental-cxx-type-aware-destroying-delete -fexceptions
 
 namespace std {
   template <class T> struct type_identity {};
diff --git a/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp b/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
index 9a90e044fe9c5c..1ff68326f05120 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++17    -fexperimental-cxx-type-aware-allocators
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++23 -fexperimental-cxx-type-aware-allocators
 
 namespace std {
   template <class T> struct type_identity {

>From 78d198d03ea6e56a7e9d138f34033dd965b3a9d7 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Wed, 13 Nov 2024 23:26:52 -0800
Subject: [PATCH 17/18] Reduce the places we pass the implicit parameters
 separately

---
 clang/include/clang/AST/ExprCXX.h             |  88 +++++----
 clang/include/clang/Sema/Sema.h               |   6 +-
 clang/lib/AST/ExprCXX.cpp                     |   9 +-
 clang/lib/CodeGen/CGExprCXX.cpp               |  41 +++--
 clang/lib/Sema/SemaCoroutine.cpp              |  80 ++++-----
 clang/lib/Sema/SemaDeclCXX.cpp                |  10 +-
 clang/lib/Sema/SemaExprCXX.cpp                | 169 +++++++++---------
 clang/lib/Serialization/ASTWriterStmt.cpp     |   5 +-
 ...aware-non-usual-deallocation-functions.cpp | 127 +++++++++++++
 9 files changed, 334 insertions(+), 201 deletions(-)
 create mode 100644 clang/test/SemaCXX/type-aware-non-usual-deallocation-functions.cpp

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 7ff710f90fb7c5..c4a1a55e5ff6f0 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2234,42 +2234,59 @@ enum class CXXNewInitializationStyle {
   Braces
 };
 
-enum class TypeAwareAllocation { Yes, No };
-
-inline TypeAwareAllocation typeAwareAllocation(bool IsTypeAware) {
-  return IsTypeAware ? TypeAwareAllocation::Yes : TypeAwareAllocation::No;
+enum class TypeAwareAllocationMode : unsigned { No, Yes };
+inline bool isTypeAwareAllocation(TypeAwareAllocationMode Mode) {
+  return Mode == TypeAwareAllocationMode::Yes;
 }
-
-enum class AlignedAllocation { Yes, No };
-
-inline AlignedAllocation alignedAllocation(bool IsAligned) {
-  return IsAligned ? AlignedAllocation::Yes : AlignedAllocation::No;
+inline TypeAwareAllocationMode
+typeAwareAllocationModeFromBool(bool IsTypeAwareAllocation) {
+  return IsTypeAwareAllocation ? TypeAwareAllocationMode::Yes
+                               : TypeAwareAllocationMode::No;
 }
 
-enum class SizedDeallocation { Yes, No };
+enum class AlignedAllocationMode : unsigned { No, Yes };
+inline bool isAlignedAllocation(AlignedAllocationMode Mode) {
+  return Mode == AlignedAllocationMode::Yes;
+}
+inline AlignedAllocationMode alignedAllocationModeFromBool(bool IsAligned) {
+  return IsAligned ? AlignedAllocationMode::Yes : AlignedAllocationMode::No;
+}
 
-inline SizedDeallocation sizedDeallocation(bool IsSized) {
-  return IsSized ? SizedDeallocation::Yes : SizedDeallocation::No;
+enum class SizedDeallocationMode : unsigned { No, Yes };
+inline bool isSizedDeallocation(SizedDeallocationMode Mode) {
+  return Mode == SizedDeallocationMode::Yes;
+}
+inline SizedDeallocationMode sizedDeallocationModeFromBool(bool IsSized) {
+  return IsSized ? SizedDeallocationMode::Yes : SizedDeallocationMode::No;
 }
 
 struct ImplicitAllocationParameters {
-  TypeAwareAllocation PassTypeIdentity;
-  AlignedAllocation PassAlignment;
-  bool passTypeIdentity() const {
-    return PassTypeIdentity == TypeAwareAllocation::Yes;
+  TypeAwareAllocationMode PassTypeIdentity;
+  AlignedAllocationMode PassAlignment;
+  unsigned getNumImplicitArgs() const {
+    unsigned Count = 1; // Size
+    if (isTypeAwareAllocation(PassTypeIdentity))
+      ++Count;
+    if (isAlignedAllocation(PassAlignment))
+      ++Count;
+    return Count;
   }
-  bool passAlignment() const { return PassAlignment == AlignedAllocation::Yes; }
 };
 
 struct ImplicitDeallocationParameters {
-  TypeAwareAllocation PassTypeIdentity;
-  AlignedAllocation PassAlignment;
-  SizedDeallocation PassSize;
-  bool passTypeIdentity() const {
-    return PassTypeIdentity == TypeAwareAllocation::Yes;
-  }
-  bool passAlignment() const { return PassAlignment == AlignedAllocation::Yes; }
-  bool passSize() const { return PassSize == SizedDeallocation::Yes; }
+  TypeAwareAllocationMode PassTypeIdentity;
+  AlignedAllocationMode PassAlignment;
+  SizedDeallocationMode PassSize;
+  unsigned getNumImplicitArgs() const {
+    unsigned Count = 1; // Size
+    if (isTypeAwareAllocation(PassTypeIdentity))
+      ++Count;
+    if (isAlignedAllocation(PassAlignment))
+      ++Count;
+    if (isSizedDeallocation(PassSize))
+      ++Count;
+    return Count;
+  }
 };
 
 /// Represents a new-expression for memory allocation and constructor
@@ -2327,7 +2344,8 @@ class CXXNewExpr final
 
   /// Build a c++ new expression.
   CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
-             FunctionDecl *OperatorDelete, ImplicitAllocationParameters IAP,
+             FunctionDecl *OperatorDelete,
+             const ImplicitAllocationParameters &IAP,
              bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
              SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
              CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2342,7 +2360,7 @@ class CXXNewExpr final
   /// Create a c++ new expression.
   static CXXNewExpr *
   Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew,
-         FunctionDecl *OperatorDelete, ImplicitAllocationParameters IAP,
+         FunctionDecl *OperatorDelete, const ImplicitAllocationParameters &IAP,
          bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
          SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
          CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2432,10 +2450,7 @@ class CXXNewExpr final
   }
 
   unsigned getNumImplicitArgs() const {
-    unsigned ImplicitArgCount = 1; // Size
-    ImplicitArgCount += passAlignment();
-    ImplicitArgCount += passTypeIdentity();
-    return ImplicitArgCount;
+    return implicitAllocationParameters().getNumImplicitArgs();
   }
 
   bool isParenTypeId() const { return CXXNewExprBits.IsParenTypeId; }
@@ -2476,12 +2491,6 @@ 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.
@@ -2492,8 +2501,9 @@ class CXXNewExpr final
   /// Provides the full set of information about expected implicit
   /// parameters in this call
   ImplicitAllocationParameters implicitAllocationParameters() const {
-    return ImplicitAllocationParameters{typeAwareAllocation(passTypeIdentity()),
-                                        alignedAllocation(passAlignment())};
+    return ImplicitAllocationParameters{
+        typeAwareAllocationModeFromBool(CXXNewExprBits.ShouldPassTypeIdentity),
+        alignedAllocationModeFromBool(CXXNewExprBits.ShouldPassAlignment)};
   }
 
   using arg_iterator = ExprIterator;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0db395c6321465..031d13d374897e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2135,8 +2135,10 @@ class Sema final : public SemaBase {
            isConstantEvaluatedOverride;
   }
 
-  bool AllowTypeAwareAllocatorsInCurrentContext() const {
-    return getLangOpts().TypeAwareAllocators && !isConstantEvaluatedContext();
+  TypeAwareAllocationMode allocationModeInCurrentContext() const {
+    if (getLangOpts().TypeAwareAllocators && !isConstantEvaluatedContext())
+      return TypeAwareAllocationMode::Yes;
+    return TypeAwareAllocationMode::No;
   }
 
   SourceLocation getLocationOfStringLiteralByte(const StringLiteral *SL,
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index cae5d3d94bae13..7613c8f7f7ce6d 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -228,7 +228,7 @@ SourceLocation CXXScalarValueInitExpr::getBeginLoc() const {
 // CXXNewExpr
 CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
                        FunctionDecl *OperatorDelete,
-                       ImplicitAllocationParameters IAP,
+                       const ImplicitAllocationParameters &IAP,
                        bool UsualArrayDeleteWantsSize,
                        ArrayRef<Expr *> PlacementArgs, SourceRange TypeIdParens,
                        std::optional<Expr *> ArraySize,
@@ -247,8 +247,9 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
 
   CXXNewExprBits.IsGlobalNew = IsGlobalNew;
   CXXNewExprBits.IsArray = ArraySize.has_value();
-  CXXNewExprBits.ShouldPassAlignment = IAP.passAlignment();
-  CXXNewExprBits.ShouldPassTypeIdentity = IAP.passTypeIdentity();
+  CXXNewExprBits.ShouldPassAlignment = isAlignedAllocation(IAP.PassAlignment);
+  CXXNewExprBits.ShouldPassTypeIdentity =
+      isTypeAwareAllocation(IAP.PassTypeIdentity);
   CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize;
   CXXNewExprBits.HasInitializer = Initializer != nullptr;
   CXXNewExprBits.StoredInitializationStyle =
@@ -293,7 +294,7 @@ CXXNewExpr::CXXNewExpr(EmptyShell Empty, bool IsArray,
 
 CXXNewExpr *CXXNewExpr::Create(
     const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew,
-    FunctionDecl *OperatorDelete, ImplicitAllocationParameters IAP,
+    FunctionDecl *OperatorDelete, const ImplicitAllocationParameters &IAP,
     bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
     SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
     CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 4e6fdd475e59f7..e0b6b5bab8ae3c 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1380,10 +1380,10 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
 namespace {
 /// The parameters to pass to a usual operator delete.
 struct UsualDeleteParams {
-  bool TypedAwareDelete = false;
+  TypeAwareAllocationMode TypeAwareDelete = TypeAwareAllocationMode::No;
   bool DestroyingDelete = false;
   bool Size = false;
-  bool Alignment = false;
+  AlignedAllocationMode Alignment = AlignedAllocationMode::No;
 };
 }
 
@@ -1396,7 +1396,7 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
   if (FD->isTypeAwareOperatorNewOrDelete()) {
     // Assume Sema has ensured a non-pointer first parameter is
     // a type identity.
-    Params.TypedAwareDelete = true;
+    Params.TypeAwareDelete = TypeAwareAllocationMode::Yes;
     assert(AI != AE);
     ++AI;
   }
@@ -1418,7 +1418,7 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
   }
 
   if (AI != AE && (*AI)->isAlignValT()) {
-    Params.Alignment = true;
+    Params.Alignment = AlignedAllocationMode::Yes;
     ++AI;
   }
 
@@ -1442,10 +1442,8 @@ namespace {
     };
 
     unsigned NumPlacementArgs : 30;
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned PassAlignmentToPlacementDelete : 1;
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned PassTypeToPlacementDelete : 1;
+    AlignedAllocationMode PassAlignmentToPlacementDelete : 1;
+    TypeAwareAllocationMode PassTypeToPlacementDelete : 1;
     const FunctionDecl *OperatorDelete;
     ValueTy Ptr;
     ValueTy AllocSize;
@@ -1462,12 +1460,12 @@ namespace {
 
     CallDeleteDuringNew(size_t NumPlacementArgs,
                         const FunctionDecl *OperatorDelete, ValueTy Ptr,
-                        ValueTy AllocSize, bool PassTypeToPlacementDelete,
-                        bool PassAlignmentToPlacementDelete,
+                        ValueTy AllocSize,
+                        const ImplicitAllocationParameters &IAP,
                         CharUnits AllocAlign)
         : NumPlacementArgs(NumPlacementArgs),
-          PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete),
-          PassTypeToPlacementDelete(PassTypeToPlacementDelete),
+          PassAlignmentToPlacementDelete(IAP.PassAlignment),
+          PassTypeToPlacementDelete(IAP.PassTypeIdentity),
           OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
           AllocAlign(AllocAlign) {}
 
@@ -1508,7 +1506,7 @@ namespace {
       // is an enum whose underlying type is std::size_t.
       // FIXME: Use the right type as the parameter type. Note that in a call
       // to operator delete(size_t, ...), we may not have it available.
-      if (Params.Alignment)
+      if (isAlignedAllocation(Params.Alignment))
         DeleteArgs.add(RValue::get(llvm::ConstantInt::get(
                            CGF.SizeTy, AllocAlign.getQuantity())),
                        CGF.getContext().getSizeType());
@@ -1549,8 +1547,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
 
     DirectCleanup *Cleanup = CGF.EHStack.pushCleanupWithExtra<DirectCleanup>(
         EHCleanup, E->getNumPlacementArgs(), E->getOperatorDelete(),
-        NewPtr.emitRawPointer(CGF), AllocSize, E->passTypeIdentity(),
-        E->passAlignment(), AllocAlign);
+        NewPtr.emitRawPointer(CGF), AllocSize,
+        E->implicitAllocationParameters(), AllocAlign);
     for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
       auto &Arg = NewArgs[I + NumNonPlacementArgs];
       Cleanup->setPlacementArg(I, Arg.getRValue(CGF), Arg.Ty);
@@ -1577,8 +1575,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
   ConditionalCleanup *Cleanup =
       CGF.EHStack.pushCleanupWithExtra<ConditionalCleanup>(
           EHCleanup, E->getNumPlacementArgs(), E->getOperatorDelete(),
-          SavedNewPtr, SavedAllocSize, E->passTypeIdentity(),
-          E->passAlignment(), AllocAlign);
+          SavedNewPtr, SavedAllocSize, E->implicitAllocationParameters(),
+          AllocAlign);
   for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
     auto &Arg = NewArgs[I + NumNonPlacementArgs];
     Cleanup->setPlacementArg(
@@ -1649,8 +1647,9 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   } else {
     const FunctionProtoType *allocatorType =
       allocator->getType()->castAs<FunctionProtoType>();
+    ImplicitAllocationParameters IAP = E->implicitAllocationParameters();
     unsigned ParamsToSkip = 0;
-    if (E->passTypeIdentity()) {
+    if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
       QualType SpecializedTypeIdentity = allocatorType->getParamType(0);
       CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
                                                SourceLocation());
@@ -1670,7 +1669,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
     }
 
     // The allocation alignment may be passed as the second argument.
-    if (E->passAlignment()) {
+    if (isAlignedAllocation(IAP.PassAlignment)) {
       QualType AlignValT = sizeType;
       if (allocatorType->getNumParams() > IndexOfAlignArg) {
         AlignValT = allocatorType->getParamType(IndexOfAlignArg);
@@ -1830,7 +1829,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   auto ParamTypeIt = DeleteFTy->param_type_begin();
 
   llvm::AllocaInst *TypeIdentityArg = nullptr;
-  if (Params.TypedAwareDelete) {
+  if (isTypeAwareAllocation(Params.TypeAwareDelete)) {
     QualType SpecializedTypeIdentity = *ParamTypeIt++;
     CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
                                              SourceLocation());
@@ -1874,7 +1873,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
   }
 
   // Pass the alignment if the delete function has an align_val_t parameter.
-  if (Params.Alignment) {
+  if (isAlignedAllocation(Params.Alignment)) {
     QualType AlignValType = *ParamTypeIt++;
     CharUnits DeleteTypeAlign =
         getContext().toCharUnitsFromBits(getContext().getTypeAlignIfKnown(
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 278e30a870b66d..9f39985a056be8 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1113,8 +1113,8 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
   // scope of the promise type. If nothing is found, a search is performed in
   // the global scope.
   ImplicitDeallocationParameters IDP = {
-      typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
-      alignedAllocation(Overaligned), SizedDeallocation::Yes};
+      S.allocationModeInCurrentContext(),
+      alignedAllocationModeFromBool(Overaligned), SizedDeallocationMode::Yes};
   if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
                                  PromiseType, IDP, /*Diagnose*/ true))
     return false;
@@ -1127,13 +1127,11 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
   //   shall be the function with one parameter.
   if (!OperatorDelete) {
     // Look for a global declaration.
-    // Coroutines can always provide their required size.
-    const bool CanProvideSize = true;
     // Sema::FindUsualDeallocationFunction will try to find the one with two
     // parameters first. It will return the deallocation function with one
     // parameter if failed.
-    IDP.PassSize =
-        CanProvideSize ? SizedDeallocation::Yes : SizedDeallocation::No;
+    // Coroutines can always provide their required size.
+    IDP.PassSize = SizedDeallocationMode::Yes;
     OperatorDelete =
         S.FindUsualDeallocationFunction(PromiseType, Loc, IDP, DeleteName);
 
@@ -1426,36 +1424,35 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper function to indicate whether the last lookup found the aligned
   // allocation function.
   ImplicitAllocationParameters IAP = {
-      typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
-      alignedAllocation(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 or via ADL for typed
-    // allocation.
-    if (NewScope == Sema::AFS_Both)
-      NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
-
-    IAP = {typeAwareAllocation(S.AllowTypeAwareAllocatorsInCurrentContext()),
-           alignedAllocation(!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);
-  };
+      S.allocationModeInCurrentContext(),
+      alignedAllocationModeFromBool(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 or via ADL for typed
+        // allocation.
+        if (NewScope == Sema::AFS_Both)
+          NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
+
+        IAP = {S.allocationModeInCurrentContext(),
+               alignedAllocationModeFromBool(
+                   !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
@@ -1478,8 +1475,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     // by passing the amount of space requested as an argument of type
     // std::size_t as the first argument, and the requested alignment as
     // an argument of type std:align_val_t as the second argument.
-    if (!OperatorNew ||
-        (S.getLangOpts().CoroAlignedAllocation && !IAP.passAlignment()))
+    if (!OperatorNew || (S.getLangOpts().CoroAlignedAllocation &&
+                         !isAlignedAllocation(IAP.PassAlignment)))
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
                                /*WithoutPlacementArgs*/ true);
   }
@@ -1504,7 +1501,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // Helper variable to emit warnings.
   bool FoundNonAlignedInPromise = false;
   if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
-    if (!OperatorNew || !IAP.passAlignment()) {
+    if (!OperatorNew || !isAlignedAllocation(IAP.PassAlignment)) {
       FoundNonAlignedInPromise = OperatorNew;
 
       LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
@@ -1599,7 +1596,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     return false;
 
   SmallVector<Expr *, 3> NewArgs;
-  if (IAP.passTypeIdentity()) {
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
     std::optional<QualType> SpecializedTypeIdentity =
         S.instantiateSpecializedTypeIdentity(PromiseType);
     if (!SpecializedTypeIdentity)
@@ -1613,7 +1610,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     NewArgs.push_back(TypeIdentity.get());
   }
   NewArgs.push_back(FrameSize);
-  if (S.getLangOpts().CoroAlignedAllocation && IAP.passAlignment())
+  if (S.getLangOpts().CoroAlignedAllocation &&
+      isAlignedAllocation(IAP.PassAlignment))
     NewArgs.push_back(FrameAlignment);
 
   if (OperatorNew->getNumParams() > NewArgs.size())
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f6c4fc136a005d..ed91a4078d2005 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9714,9 +9714,9 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
     QualType DeallocType = Context.getRecordType(RD);
     DeclarationName Name =
       Context.DeclarationNames.getCXXOperatorName(OO_Delete);
-    ImplicitDeallocationParameters IDP = {
-        typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
-        AlignedAllocation::No, SizedDeallocation::No};
+    ImplicitDeallocationParameters IDP = {allocationModeInCurrentContext(),
+                                          AlignedAllocationMode::No,
+                                          SizedDeallocationMode::No};
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
                                  OperatorDelete, DeallocType, IDP,
                                  /*Diagnose*/ false)) {
@@ -16159,7 +16159,7 @@ bool Sema::isTypeAwareOperatorNewOrDelete(const NamedDecl *ND) const {
 std::optional<FunctionDecl *>
 Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
                                       QualType DeallocType) {
-  if (!AllowTypeAwareAllocatorsInCurrentContext())
+  if (!isTypeAwareAllocation(allocationModeInCurrentContext()))
     return std::nullopt;
 
   TemplateParameterList *TemplateParameters =
@@ -16234,7 +16234,7 @@ Sema::instantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
 
 std::optional<QualType>
 Sema::instantiateSpecializedTypeIdentity(QualType Subject) {
-  assert(AllowTypeAwareAllocatorsInCurrentContext());
+  assert(clang::isTypeAwareAllocation(allocationModeInCurrentContext()));
   ClassTemplateDecl *TypeIdentity = getStdTypeIdentity();
   if (!TypeIdentity)
     return std::nullopt;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 57a32a7bc5753e..464a57c3ed1650 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1754,7 +1754,7 @@ static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
     return false;
 
   unsigned UsualParams = 1;
-  if (S.AllowTypeAwareAllocatorsInCurrentContext() &&
+  if (isTypeAwareAllocation(S.allocationModeInCurrentContext()) &&
       UsualParams < FD->getNumParams() && S.isTypeAwareOperatorNewOrDelete(FD))
     ++UsualParams;
 
@@ -1778,9 +1778,9 @@ namespace {
     UsualDeallocFnInfo() : Found(), FD(nullptr) {}
     UsualDeallocFnInfo(Sema &S, DeclAccessPair Found, QualType AllocType)
         : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
-          Destroying(false), SizedDelete(SizedDeallocation::No),
-          AlignedDelete(AlignedAllocation::No),
-          TypeAwareDelete(TypeAwareAllocation::No),
+          Destroying(false),
+          IDP({TypeAwareAllocationMode::No, AlignedAllocationMode::No,
+               SizedDeallocationMode::No}),
           CUDAPref(SemaCUDA::CFP_Native) {
       // A function template declaration is only a usual deallocation function
       // if it is a typed delete.
@@ -1795,7 +1795,7 @@ namespace {
         FD = *InstantiatedDecl;
       }
       unsigned NumBaseParams = 1;
-      if (S.AllowTypeAwareAllocatorsInCurrentContext() &&
+      if (isTypeAwareAllocation(S.allocationModeInCurrentContext()) &&
           S.isTypeAwareOperatorNewOrDelete(FD)) {
         QualType TypeIdentityTag = FD->getParamDecl(0)->getType();
         std::optional<QualType> ExpectedTypeIdentityTag =
@@ -1808,7 +1808,7 @@ namespace {
           FD = nullptr;
           return;
         }
-        TypeAwareDelete = TypeAwareAllocation::Yes;
+        IDP.PassTypeIdentity = TypeAwareAllocationMode::Yes;
         ++NumBaseParams;
       }
 
@@ -1822,13 +1822,13 @@ namespace {
               FD->getParamDecl(NumBaseParams)->getType(),
               S.Context.getSizeType())) {
         ++NumBaseParams;
-        SizedDelete = SizedDeallocation::Yes;
+        IDP.PassSize = SizedDeallocationMode::Yes;
       }
 
       if (NumBaseParams < FD->getNumParams() &&
           FD->getParamDecl(NumBaseParams)->getType()->isAlignValT()) {
         ++NumBaseParams;
-        AlignedDelete = AlignedAllocation::Yes;
+        IDP.PassAlignment = AlignedAllocationMode::Yes;
       }
 
       // In CUDA, determine how much we'd like / dislike to call this.
@@ -1839,35 +1839,30 @@ namespace {
 
     explicit operator bool() const { return FD; }
 
-    bool isTypeAware() const {
-      return TypeAwareDelete == TypeAwareAllocation::Yes;
-    }
-    bool isAligned() const { return AlignedDelete == AlignedAllocation::Yes; }
-    bool isSized() const { return SizedDelete == SizedDeallocation::Yes; }
-
     int Compare(Sema &S, const UsualDeallocFnInfo &Other,
-                ImplicitDeallocationParameters IDP) const {
+                ImplicitDeallocationParameters TargetIDP) const {
       // C++ P0722:
       //   A destroying operator delete is preferred over a non-destroying
       //   operator delete.
       if (Destroying != Other.Destroying)
         return Destroying ? 1 : -1;
 
+      const ImplicitDeallocationParameters &OtherIDP = Other.IDP;
       // Selection for type awareness has priority over alignment and size
-      if (isTypeAware() != Other.isTypeAware())
-        return isTypeAware() == IDP.passTypeIdentity() ? 1 : -1;
+      if (IDP.PassTypeIdentity != OtherIDP.PassTypeIdentity)
+        return IDP.PassTypeIdentity == TargetIDP.PassTypeIdentity ? 1 : -1;
 
       // C++17 [expr.delete]p10:
       //   If the type has new-extended alignment, a function with a parameter
       //   of type std::align_val_t is preferred; otherwise a function without
       //   such a parameter is preferred
-      if (isAligned() != Other.isAligned())
-        return isAligned() == IDP.passAlignment() ? 1 : -1;
+      if (IDP.PassAlignment != OtherIDP.PassAlignment)
+        return IDP.PassAlignment == TargetIDP.PassAlignment ? 1 : -1;
 
-      if (isSized() != Other.isSized())
-        return isSized() == IDP.passSize() ? 1 : -1;
+      if (IDP.PassSize != OtherIDP.PassSize)
+        return IDP.PassSize == TargetIDP.PassSize ? 1 : -1;
 
-      if (isTypeAware()) {
+      if (isTypeAwareAllocation(IDP.PassTypeIdentity)) {
         // Type aware allocation involves templates so we need to choose
         // the best type
         FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
@@ -1880,8 +1875,7 @@ namespace {
           const auto *DC = dyn_cast<CXXRecordDecl>(Found->getDeclContext());
           const auto *OtherDC =
               dyn_cast<CXXRecordDecl>(Other.Found->getDeclContext());
-          unsigned ImplicitArgCount =
-              1 + Destroying + isTypeAware() + isAligned() + isSized();
+          unsigned ImplicitArgCount = Destroying + IDP.getNumImplicitArgs();
           if (FunctionTemplateDecl *Best = S.getMoreSpecializedTemplate(
                   PrimaryTemplate, OtherPrimaryTemplate, SourceLocation(),
                   TPOC_Call, ImplicitArgCount,
@@ -1904,9 +1898,7 @@ namespace {
     DeclAccessPair Found;
     FunctionDecl *FD;
     bool Destroying;
-    SizedDeallocation SizedDelete;
-    AlignedAllocation AlignedDelete;
-    TypeAwareAllocation TypeAwareDelete;
+    ImplicitDeallocationParameters IDP;
     SemaCUDA::CUDAFunctionPreference CUDAPref;
   };
 }
@@ -1989,7 +1981,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,
-                                         TypeAwareAllocation PassType,
+                                         TypeAwareAllocationMode PassType,
                                          QualType allocType) {
   const RecordType *record =
     allocType->getBaseElementTypeUnsafe()->getAs<RecordType>();
@@ -2016,10 +2008,11 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
   //   If the deallocation functions have class scope, the one without a
   //   parameter of type std::size_t is selected.
   ImplicitDeallocationParameters IDP = {
-      PassType, alignedAllocation(hasNewExtendedAlignment(S, allocType)),
-      SizedDeallocation::No};
+      PassType,
+      alignedAllocationModeFromBool(hasNewExtendedAlignment(S, allocType)),
+      SizedDeallocationMode::No};
   auto Best = resolveDeallocationOverload(S, ops, IDP, allocType);
-  return Best && Best.isSized();
+  return Best && isSizedDeallocation(Best.IDP.PassSize);
 }
 
 ExprResult
@@ -2432,9 +2425,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
       AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
   unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
   ImplicitAllocationParameters IAP = {
-      typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
-      alignedAllocation(getLangOpts().AlignedAllocation &&
-                        Alignment > NewAlignment)};
+      allocationModeInCurrentContext(),
+      alignedAllocationModeFromBool(getLangOpts().AlignedAllocation &&
+                                    Alignment > NewAlignment)};
 
   if (CheckArgsForPlaceholders(PlacementArgs))
     return ExprError();
@@ -2466,11 +2459,11 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     // argument. Skip the second parameter too if we're passing in the
     // alignment; we've already filled it in.
     unsigned NumImplicitArgs = 1;
-    if (IAP.passTypeIdentity()) {
+    if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
       assert(OperatorNew->isTypeAwareOperatorNewOrDelete());
       NumImplicitArgs++;
     }
-    if (IAP.passAlignment())
+    if (isAlignedAllocation(IAP.PassAlignment))
       NumImplicitArgs++;
     if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto,
                                NumImplicitArgs, PlacementArgs, AllPlaceArgs,
@@ -2536,7 +2529,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     CallArgs.emplace_back(AllocationSize
                               ? static_cast<Expr *>(&AllocationSizeLiteral)
                               : &OpaqueAllocationSize);
-    if (IAP.passAlignment())
+    if (isAlignedAllocation(IAP.PassAlignment))
       CallArgs.emplace_back(&DesiredAlignment);
     CallArgs.insert(CallArgs.end(), PlacementArgs.begin(), PlacementArgs.end());
 
@@ -2547,7 +2540,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() && !IAP.passAlignment() &&
+    if (PlacementArgs.empty() && !isAlignedAllocation(IAP.PassAlignment) &&
         (OperatorNew->isImplicit() ||
          (OperatorNew->getBeginLoc().isValid() &&
           getSourceManager().isInSystemHeader(OperatorNew->getBeginLoc())))) {
@@ -2680,7 +2673,7 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
 enum class ResolveMode { Typed, Untyped };
 static bool resolveAllocationOverloadInterior(
     Sema &S, LookupResult &R, SourceRange Range, ResolveMode Mode,
-    SmallVectorImpl<Expr *> &Args, AlignedAllocation &PassAlignment,
+    SmallVectorImpl<Expr *> &Args, AlignedAllocationMode &PassAlignment,
     FunctionDecl *&Operator, OverloadCandidateSet *AlignedCandidates,
     Expr *AlignArg, bool Diagnose) {
   unsigned NonTypeArgumentOffset = 0;
@@ -2729,8 +2722,8 @@ static bool resolveAllocationOverloadInterior(
     //   If no matching function is found and the allocated object type has
     //   new-extended alignment, the alignment argument is removed from the
     //   argument list, and overload resolution is performed again.
-    if (PassAlignment == AlignedAllocation::Yes) {
-      PassAlignment = AlignedAllocation::No;
+    if (isAlignedAllocation(PassAlignment)) {
+      PassAlignment = AlignedAllocationMode::No;
       AlignArg = Args[NonTypeArgumentOffset + 1];
       Args.erase(Args.begin() + NonTypeArgumentOffset + 1);
       return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
@@ -2863,12 +2856,12 @@ static bool resolveAllocationOverload(
     ImplicitAllocationParameters &IAP, FunctionDecl *&Operator,
     OverloadCandidateSet *AlignedCandidates, Expr *AlignArg, bool Diagnose) {
   Operator = nullptr;
-  if (IAP.passTypeIdentity()) {
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
     assert(Args[0]->getType()->isTypeIdentitySpecialization());
     SmallVector<Expr *> UntypedParameters;
     UntypedParameters.reserve(Args.size() - 1);
     UntypedParameters.append(Args.begin() + 1, Args.end());
-    AlignedAllocation InitialAlignmentMode = IAP.PassAlignment;
+    AlignedAllocationMode InitialAlignmentMode = IAP.PassAlignment;
     if (resolveAllocationOverloadInterior(
             S, R, Range, ResolveMode::Typed, Args, IAP.PassAlignment, Operator,
             AlignedCandidates, AlignArg, Diagnose))
@@ -2876,7 +2869,7 @@ static bool resolveAllocationOverload(
     if (Operator)
       return false;
     // There's no type aware allocator
-    IAP.PassTypeIdentity = TypeAwareAllocation::No;
+    IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
     // Restore alignment requirements
     IAP.PassAlignment = InitialAlignmentMode;
     // Finally prepare the type free parameter list
@@ -2905,8 +2898,7 @@ bool Sema::FindAllocationFunctions(
   //   placement form.
 
   SmallVector<Expr*, 8> AllocArgs;
-  unsigned ImplicitArgCount = 1 + IAP.passAlignment() + IAP.passTypeIdentity();
-  AllocArgs.reserve(ImplicitArgCount + PlaceArgs.size());
+  AllocArgs.reserve(IAP.getNumImplicitArgs() + PlaceArgs.size());
 
   // C++ [expr.new]p8:
   //   If the allocated type is a non-array type, the allocation
@@ -2927,18 +2919,18 @@ bool Sema::FindAllocationFunctions(
   // 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 (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
     if (std::optional<QualType> SpecializedTypeIdentity =
             instantiateSpecializedTypeIdentity(AllocElemType)) {
       TypeIdentity = *SpecializedTypeIdentity;
     } else {
-      IAP.PassTypeIdentity = TypeAwareAllocation::No;
+      IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
     }
   }
-  TypeAwareAllocation OriginalTypeAwareState = IAP.PassTypeIdentity;
+  TypeAwareAllocationMode OriginalTypeAwareState = IAP.PassTypeIdentity;
 
   CXXScalarValueInitExpr TypeIdentityParam(TypeIdentity, nullptr, StartLoc);
-  if (IAP.passTypeIdentity())
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity))
     AllocArgs.push_back(&TypeIdentityParam);
 
   QualType SizeTy = Context.getSizeType();
@@ -2948,12 +2940,12 @@ bool Sema::FindAllocationFunctions(
   AllocArgs.push_back(&Size);
 
   QualType AlignValT = Context.VoidTy;
-  if (IAP.passAlignment()) {
+  if (isAlignedAllocation(IAP.PassAlignment)) {
     DeclareGlobalNewDelete();
     AlignValT = Context.getTypeDeclType(getStdAlignValT());
   }
   CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
-  if (IAP.passAlignment())
+  if (isAlignedAllocation(IAP.PassAlignment))
     AllocArgs.push_back(&Align);
 
   AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end());
@@ -3059,10 +3051,9 @@ bool Sema::FindAllocationFunctions(
       return true;
 
     DeclareGlobalNewDelete();
-    DeallocLookupMode LookupMode =
-        OriginalTypeAwareState == TypeAwareAllocation::Yes
-            ? DeallocLookupMode::OptionallyTyped
-            : DeallocLookupMode::Untyped;
+    DeallocLookupMode LookupMode = isTypeAwareAllocation(OriginalTypeAwareState)
+                                       ? DeallocLookupMode::OptionallyTyped
+                                       : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                       DeleteName, AllocElemType);
   }
@@ -3086,7 +3077,7 @@ bool Sema::FindAllocationFunctions(
   // type uses the sized or non-sized form of aligned operator delete.
 
   unsigned NonPlacementNewArgCount = 1; // size parameter
-  if (IAP.passTypeIdentity())
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity))
     ++NonPlacementNewArgCount;
   bool isPlacementNew = !PlaceArgs.empty() ||
                         OperatorNew->param_size() != NonPlacementNewArgCount ||
@@ -3108,7 +3099,7 @@ bool Sema::FindAllocationFunctions(
       auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
 
       SmallVector<QualType, 4> ArgTypes;
-      if (IAP.passTypeIdentity())
+      if (isTypeAwareAllocation(IAP.PassTypeIdentity))
         ArgTypes.push_back(TypeIdentity);
       ArgTypes.push_back(Context.VoidPtrTy);
       for (unsigned I = ArgTypes.size(), N = Proto->getNumParams(); I < N; ++I)
@@ -3158,8 +3149,9 @@ bool Sema::FindAllocationFunctions(
     llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns;
     ImplicitDeallocationParameters IDP = {
         OriginalTypeAwareState,
-        alignedAllocation(hasNewExtendedAlignment(*this, AllocElemType)),
-        FoundGlobalDelete ? SizedDeallocation::Yes : SizedDeallocation::No};
+        alignedAllocationModeFromBool(
+            hasNewExtendedAlignment(*this, AllocElemType)),
+        sizedDeallocationModeFromBool(FoundGlobalDelete)};
     UsualDeallocFnInfo Selected = resolveDeallocationOverload(
         *this, FoundDelete, IDP, AllocElemType, &BestDeallocFns);
     if (Selected && BestDeallocFns.empty())
@@ -3179,7 +3171,7 @@ bool Sema::FindAllocationFunctions(
   if (Matches.size() == 1) {
     OperatorDelete = Matches[0].second;
     if (isTypeAwareOperatorNewOrDelete(OperatorDelete) !=
-        IAP.passTypeIdentity()) {
+        (isTypeAwareAllocation(IAP.PassTypeIdentity))) {
       Diag(StartLoc, diag::warn_mismatching_type_aware_cleanup_deallocator);
       int NewDiagIndex = isTypeAwareOperatorNewOrDelete(OperatorNew) ? 0 : 1;
       int DeleteDiagIndex =
@@ -3190,7 +3182,7 @@ bool Sema::FindAllocationFunctions(
            diag::note_type_aware_operator_declared)
           << DeleteDiagIndex << OperatorDelete;
     }
-    if (IAP.passTypeIdentity() &&
+    if ((isTypeAwareAllocation(IAP.PassTypeIdentity)) &&
         OperatorDelete->getDeclContext() != OperatorNew->getDeclContext()) {
       Diag(StartLoc,
            diag::err_no_matching_type_aware_cleanup_deallocator_mismatch)
@@ -3216,14 +3208,16 @@ bool Sema::FindAllocationFunctions(
       //   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.SizedDelete == SizedDeallocation::Yes;
+      bool IsSizedDelete = isSizedDeallocation(Info.IDP.PassSize);
       if (IsSizedDelete && !FoundGlobalDelete) {
         ImplicitDeallocationParameters SizeTestingIDP = {
-            Info.TypeAwareDelete, Info.AlignedDelete, SizedDeallocation::No};
+            Info.IDP.PassTypeIdentity, Info.IDP.PassAlignment,
+            SizedDeallocationMode::No};
         auto NonSizedDelete = resolveDeallocationOverload(
             *this, FoundDelete, SizeTestingIDP, AllocElemType);
-        if (NonSizedDelete && !NonSizedDelete.isSized() &&
-            NonSizedDelete.AlignedDelete == Info.AlignedDelete)
+        if (NonSizedDelete &&
+            !isSizedDeallocation(NonSizedDelete.IDP.PassSize) &&
+            NonSizedDelete.IDP.PassAlignment == Info.IDP.PassAlignment)
           IsSizedDelete = false;
       }
 
@@ -3509,9 +3503,10 @@ FunctionDecl *Sema::FindUsualDeallocationFunction(
   DeclareGlobalNewDelete();
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
-  DeallocLookupMode LookupMode = AllowTypeAwareAllocatorsInCurrentContext()
-                                     ? DeallocLookupMode::OptionallyTyped
-                                     : DeallocLookupMode::Untyped;
+  DeallocLookupMode LookupMode =
+      isTypeAwareAllocation(allocationModeInCurrentContext())
+          ? DeallocLookupMode::OptionallyTyped
+          : DeallocLookupMode::Untyped;
   LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                     Name, DeallocType);
 
@@ -3539,9 +3534,9 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
 
   FunctionDecl *OperatorDelete = nullptr;
   QualType DeallocType = Context.getRecordType(RD);
-  ImplicitDeallocationParameters IDP = {
-      typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
-      AlignedAllocation::No, SizedDeallocation::No};
+  ImplicitDeallocationParameters IDP = {allocationModeInCurrentContext(),
+                                        AlignedAllocationMode::No,
+                                        SizedDeallocationMode::No};
 
   if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, DeallocType, IDP))
     return nullptr;
@@ -3553,8 +3548,8 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
   // non-array delete.
   QualType RecordType = Context.getRecordType(RD);
   IDP.PassAlignment =
-      alignedAllocation(hasNewExtendedAlignment(*this, RecordType));
-  IDP.PassSize = SizedDeallocation::Yes;
+      alignedAllocationModeFromBool(hasNewExtendedAlignment(*this, RecordType));
+  IDP.PassSize = SizedDeallocationMode::Yes;
   return FindUsualDeallocationFunction(RecordType, Loc, IDP, Name);
 }
 
@@ -3573,9 +3568,9 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
 
   Found.suppressDiagnostics();
 
-  if (!IDP.passAlignment() &&
+  if (!isAlignedAllocation(IDP.PassAlignment) &&
       hasNewExtendedAlignment(*this, Context.getRecordType(RD)))
-    IDP.PassAlignment = AlignedAllocation::Yes;
+    IDP.PassAlignment = AlignedAllocationMode::Yes;
 
   // C++17 [expr.delete]p10:
   //   If the deallocation functions have class scope, the one without a
@@ -4011,9 +4006,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
                                       ArrayForm ? OO_Array_Delete : OO_Delete);
 
     if (PointeeRD) {
-      ImplicitDeallocationParameters IDP = {
-          typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
-          AlignedAllocation::No, SizedDeallocation::No};
+      ImplicitDeallocationParameters IDP = {allocationModeInCurrentContext(),
+                                            AlignedAllocationMode::No,
+                                            SizedDeallocationMode::No};
       if (!UseGlobal &&
           FindDeallocationFunction(StartLoc, PointeeRD, DeleteName,
                                    OperatorDelete, Pointee, IDP))
@@ -4030,12 +4025,11 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
 
         // 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),
-                  Pointee)
-                  .isSized();
+        else if (isa_and_nonnull<CXXMethodDecl>(OperatorDelete)) {
+          UsualDeallocFnInfo UDFI(
+              *this, DeclAccessPair::make(OperatorDelete, AS_public), Pointee);
+          UsualArrayDeleteWantsSize = isSizedDeallocation(UDFI.IDP.PassSize);
+        }
       }
 
       if (!PointeeRD->hasIrrelevantDestructor())
@@ -4066,8 +4060,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
 
       // Look for a global declaration.
       ImplicitDeallocationParameters IDP = {
-          typeAwareAllocation(AllowTypeAwareAllocatorsInCurrentContext()),
-          alignedAllocation(Overaligned), sizedDeallocation(CanProvideSize)};
+          allocationModeInCurrentContext(),
+          alignedAllocationModeFromBool(Overaligned),
+          sizedDeallocationModeFromBool(CanProvideSize)};
       OperatorDelete =
           FindUsualDeallocationFunction(Pointee, StartLoc, IDP, DeleteName);
       if (!OperatorDelete)
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 5bd38d5f17f3db..0f3207171dfbd4 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1919,8 +1919,9 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
   Record.push_back(E->isParenTypeId());
 
   Record.push_back(E->isGlobalNew());
-  Record.push_back(E->passAlignment());
-  Record.push_back(E->passTypeIdentity());
+  ImplicitAllocationParameters IAP = E->implicitAllocationParameters();
+  Record.push_back(isAlignedAllocation(IAP.PassAlignment));
+  Record.push_back(isTypeAwareAllocation(IAP.PassTypeIdentity));
   Record.push_back(E->doesUsualArrayDeleteWantSize());
   Record.push_back(E->CXXNewExprBits.HasInitializer);
   Record.push_back(E->CXXNewExprBits.StoredInitializationStyle);
diff --git a/clang/test/SemaCXX/type-aware-non-usual-deallocation-functions.cpp b/clang/test/SemaCXX/type-aware-non-usual-deallocation-functions.cpp
new file mode 100644
index 00000000000000..7e38205d7e766a
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-non-usual-deallocation-functions.cpp
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c++23 -faligned-allocation -fexperimental-cxx-type-aware-allocators -fexperimental-cxx-type-aware-destroying-delete
+
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t {};
+}
+
+using size_t = __SIZE_TYPE__;
+
+
+struct __attribute__((aligned(128))) S1 {
+    S1();
+    template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, U);
+    template <typename T, typename U> void operator delete(std::type_identity<T>, void*, U); // #1
+};
+struct __attribute__((aligned(128))) S2 {
+    S2();
+    template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+    template <typename T, typename U> void operator delete(std::type_identity<T>, void*, U); // #2
+};
+
+struct __attribute__((aligned(128))) S3 {
+    S3();
+    template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, U);
+    template <typename T> void operator delete(std::type_identity<T>, void*, std::align_val_t);
+};
+
+struct __attribute__((aligned(128))) S4 {
+    S4();
+};
+
+template <typename U> void *operator new(std::type_identity<S4>, size_t, U);
+template <typename U> void operator delete(std::type_identity<S4>, void*, U);
+// We use this deleted operator delete to verify we skip the above decl with U=align_val_t
+void operator delete(std::type_identity<S4>, void*, std::align_val_t) = delete; // #3
+
+template <typename AlignValT>
+struct __attribute__((aligned(128))) S5 {
+    S5();
+    template <typename T> void *operator new(std::type_identity<T>, size_t, AlignValT);
+    template <typename T> void operator delete(std::type_identity<T>, void*, AlignValT); // #4
+};
+
+struct __attribute__((aligned(128))) S6 {
+    S6();
+    template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+    template <typename T> void operator delete(std::type_identity<T>, void*, std::align_val_t); // #5
+    template <typename T> void operator delete(std::type_identity<T>, S6*, std::destroying_delete_t, std::align_val_t); // #6
+};
+
+struct __attribute__((aligned(128))) S7 {
+    S7();
+    template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+    template <typename T> void operator delete(std::type_identity<T>, void*, std::align_val_t); // #7
+    template <typename T, typename U> void operator delete(std::type_identity<T>, S7*, std::destroying_delete_t, U); // #8
+    // expected-error at -1 {{destroying operator delete can have only an optional size and optional alignment parameter}}
+};
+
+template <typename AlignValT>
+struct __attribute__((aligned(128))) S8 {
+    S8();
+    template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+    template <typename T> void operator delete(std::type_identity<T>, void*, std::align_val_t); // #9
+    template <typename T> void operator delete(std::type_identity<T>, S8*, std::destroying_delete_t, AlignValT); // #10
+};
+
+extern "C" void test1() {
+    S1 *s = new S1;
+    delete s;
+    // expected-error at -1 {{no suitable member 'operator delete' in 'S1'}}
+    // expected-note@#1 {{member 'operator delete' declared here}}
+}
+
+extern "C" void test2() {
+    S2 *s = new S2;
+    delete s;
+    // expected-error at -1 {{no suitable member 'operator delete' in 'S2'}}
+    // expected-note@#2 {{member 'operator delete' declared here}}
+}
+
+extern "C" void test3() {
+    S3 *s = new S3;
+    delete s;
+}
+
+extern "C" void test4() {
+    S4 *s = new S4;
+    delete s;
+    // expected-error at -1 {{attempt to use a deleted function}}
+    // expected-note@#3 {{'operator delete' has been explicitly marked deleted here}}
+}
+
+extern "C" void test5() {
+    S5<std::align_val_t> *s = new S5<std::align_val_t>;
+    delete s;
+}
+
+extern "C" void test6() {
+    S6 *s = new S6;
+    delete s;
+}
+
+template <typename T> void test7_inner() {
+    struct Inner {
+        void *operator new(std::type_identity<Inner>, size_t);
+        void operator delete(std::type_identity<Inner>, Inner*, std::destroying_delete_t, T);
+    };
+    Inner *obj = new Inner;
+    delete obj;
+}
+
+void test7() {
+    test7_inner<std::align_val_t>();
+}
+
+// extern "C" void test7() {
+//     S7 *s = new S7;
+//     delete s;
+// }
+
+// extern "C" void test8() {
+//     S8<std::align_val_t> *s = new S8<std::align_val_t>;
+//     delete s;
+// }

>From 90011a0222282bf724ba49b162096c9aa17dc404 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Wed, 13 Nov 2024 23:26:52 -0800
Subject: [PATCH 18/18] Added codegen tests

This did find a few places where we were not forcing full instantiation
of the type_identity specialization when calling the clean up method in
an environment where the relevant new and delete operators do not have
matching type-awareness.
---
 clang/lib/CodeGen/CGExprCXX.cpp               |  32 ++-
 clang/lib/Sema/SemaExprCXX.cpp                |  12 +-
 .../test/CodeGenCXX/type-aware-allocators.cpp | 203 ++++++++++++++++++
 .../type-aware-placement-operators.cpp        |  41 ++++
 4 files changed, 276 insertions(+), 12 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/type-aware-allocators.cpp
 create mode 100644 clang/test/SemaCXX/type-aware-placement-operators.cpp

diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index e0b6b5bab8ae3c..ef58238919dda7 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1442,8 +1442,10 @@ namespace {
     };
 
     unsigned NumPlacementArgs : 30;
-    AlignedAllocationMode PassAlignmentToPlacementDelete : 1;
-    TypeAwareAllocationMode PassTypeToPlacementDelete : 1;
+    LLVM_PREFERRED_TYPE(AlignedAllocationMode)
+    unsigned PassAlignmentToPlacementDelete : 1;
+    LLVM_PREFERRED_TYPE(TypeAwareAllocationMode)
+    unsigned PassTypeToPlacementDelete : 1;
     const FunctionDecl *OperatorDelete;
     ValueTy Ptr;
     ValueTy AllocSize;
@@ -1464,10 +1466,10 @@ namespace {
                         const ImplicitAllocationParameters &IAP,
                         CharUnits AllocAlign)
         : NumPlacementArgs(NumPlacementArgs),
-          PassAlignmentToPlacementDelete(IAP.PassAlignment),
-          PassTypeToPlacementDelete(IAP.PassTypeIdentity),
+          PassAlignmentToPlacementDelete(isAlignedAllocation(IAP.PassAlignment)),
           OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
-          AllocAlign(AllocAlign) {}
+          AllocAlign(AllocAlign) {
+          }
 
     void setPlacementArg(unsigned I, RValueTy Arg, QualType Type) {
       assert(I < NumPlacementArgs && "index out of range");
@@ -1477,17 +1479,27 @@ namespace {
     void Emit(CodeGenFunction &CGF, Flags flags) override {
       const auto *FPT = OperatorDelete->getType()->castAs<FunctionProtoType>();
       CallArgList DeleteArgs;
-
-      // The first argument is always a void* (or C* for a destroying operator
-      // delete for class type C).
-      DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0));
+      unsigned FirstNonTypeArg = 0;
+      TypeAwareAllocationMode TypeAwareDeallocation = TypeAwareAllocationMode::No;
+      if (OperatorDelete->isTypeAwareOperatorNewOrDelete()) {
+        TypeAwareDeallocation = TypeAwareAllocationMode::Yes;
+        QualType SpecializedTypeIdentity = FPT->getParamType(0);
+        ++FirstNonTypeArg;
+        CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr, SourceLocation());
+        DeleteArgs.add(CGF.EmitAnyExprToTemp(&TypeIdentityParam),
+                          SpecializedTypeIdentity);
+      }
+      // The first non type tag argument is always a void* (or C* for a destroying
+      // operator  delete for class type C).
+      DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(FirstNonTypeArg));
 
       // Figure out what other parameters we should be implicitly passing.
       UsualDeleteParams Params;
       if (NumPlacementArgs) {
         // A placement deallocation function is implicitly passed an alignment
         // if the placement allocation function was, but is never passed a size.
-        Params.Alignment = PassAlignmentToPlacementDelete;
+        Params.Alignment = alignedAllocationModeFromBool(PassAlignmentToPlacementDelete);
+        Params.TypeAwareDelete = TypeAwareDeallocation;
       } else {
         // For a non-placement new-expression, 'operator delete' can take a
         // size and/or an alignment if it has the right parameters.
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5f11a63cb29ad0..a9f55c66501fe4 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1942,7 +1942,7 @@ static bool CheckDeleteOperator(Sema &S, SourceLocation StartLoc,
 /// 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, ImplicitDeallocationParameters IDP,
+    Sema &S, LookupResult &R, const ImplicitDeallocationParameters& IDP,
     QualType DeallocType,
     llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) {
 
@@ -2923,6 +2923,9 @@ bool Sema::FindAllocationFunctions(
     if (std::optional<QualType> SpecializedTypeIdentity =
             instantiateSpecializedTypeIdentity(AllocElemType)) {
       TypeIdentity = *SpecializedTypeIdentity;
+      if (RequireCompleteType(StartLoc, TypeIdentity,
+                                diag::err_incomplete_type))
+      return true;
     } else {
       IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
     }
@@ -4092,8 +4095,13 @@ 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)) {
+      QualType TypeIdentity = OperatorDelete->getParamDecl(0)->getType();
+      if (RequireCompleteType(StartLoc, TypeIdentity,
+                                diag::err_incomplete_type))
+        return ExprError();
       PointeeIndex = 1;
+    }
     QualType ParamType = OperatorDelete->getParamDecl(PointeeIndex)->getType();
     if (!IsVirtualDelete && !ParamType->getPointeeType()->isVoidType()) {
       Qualifiers Qs = Pointee.getQualifiers();
diff --git a/clang/test/CodeGenCXX/type-aware-allocators.cpp b/clang/test/CodeGenCXX/type-aware-allocators.cpp
new file mode 100644
index 00000000000000..abe33b527a9146
--- /dev/null
+++ b/clang/test/CodeGenCXX/type-aware-allocators.cpp
@@ -0,0 +1,203 @@
+// RUN: %clang_cc1 %s -triple arm64-apple-macosx -faligned-allocation -fexperimental-cxx-type-aware-allocators -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -Wno-type-aware-allocator-mismatch -o - | FileCheck %s
+
+
+namespace std {
+  template <class T> struct type_identity {
+    typedef T type;
+  };
+  enum class align_val_t : __SIZE_TYPE__ {};
+  struct destroying_delete_t {};
+}
+
+using size_t = __SIZE_TYPE__;
+
+// Sanity check to esure the semantics of the selected compiler mode
+// will trigger the exception handlers we are expecting, before
+// involving type aware allocation.
+// We duplicate the struct definitions so we don't trigger diagnostics
+// for changing operator resolution on the same type, and we do the
+// untyped test before specifying the typed operators rather than using
+// template constraints so we don't have to deal with monstrous mangling.
+struct S1 {
+  S1();
+};
+
+struct __attribute__((aligned(128))) S2 {
+  S2();
+};
+
+struct S3 {
+  S3();
+};
+
+struct __attribute__((aligned(128))) S4 {
+  S4();
+};
+
+extern "C" void test_no_type_aware_allocator() {
+  S1 *s1 = new S1;
+  delete s1;
+  S2 *s2 = new S2;
+  delete s2;
+}
+// CHECK-LABEL: test_no_type_aware_allocator
+// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
+// CHECK: @_ZN2S1C1Ev({{.*}} [[ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S1LPAD:lpad]]
+// CHECK: @_ZdlPvm(
+// CHECK: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwmSt11align_val_t(
+// CHECK: _ZN2S2C1Ev({{.*}} [[ALIGNED_ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S2LPAD:lpad3]]
+// CHECK: _ZdlPvmSt11align_val_t(
+// CHECK: [[S1LPAD]]:{{.*}};
+// CHECK: @_ZdlPvm({{.*}}[[ALLOC_RESULT]], {{.*}})
+// CHECK: [[S2LPAD]]:
+// CHECK: _ZdlPvSt11align_val_t({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
+
+template <typename T> void *operator new(std::type_identity<T>, size_t);
+template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+template <typename T> void operator delete(std::type_identity<T>, void *);
+template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t);
+
+extern "C" void test_free_type_aware_allocator() {
+  S3 *s3 = new S3;
+  delete s3;
+  S4 *s4 = new S4;
+  delete s4;
+}
+// CHECK-LABEL: test_free_type_aware_allocator
+// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwI2S3EPvSt13type_identityIT_Em(
+// CHECK: @_ZN2S3C1Ev({{.*}}[[ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S3LPAD:.*]]
+// CHECK: @_ZdlI2S3EvSt13type_identityIT_EPv(
+// CHECK: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwI2S4EPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S4C1Ev({{.*}}[[ALIGNED_ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S4LPAD:.*]]
+// CHECK: @_ZdlI2S4EvSt13type_identityIT_EPvSt11align_val_t(
+// CHECK: [[S3LPAD]]:
+// CHECK: @_ZdlI2S3EvSt13type_identityIT_EPv({{.*}}, {{.*}}[[ALLOC_RESULT]])
+// CHECK: [[S4LPAD]]:
+// CHECK: @_ZdlI2S4EvSt13type_identityIT_EPvSt11align_val_t({{.*}}, {{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
+
+struct S5 {
+  S5();
+  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(S5*, std::destroying_delete_t);
+};
+
+extern "C" void test_ensure_type_aware_cleanup() {
+  S5 *s5 = new S5;
+  delete s5;
+}
+// CHECK-LABEL: test_ensure_type_aware_cleanup
+// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_ZN2S5nwIS_EEPvSt13type_identityIT_Em(
+// CHECK: @_ZN2S5C1Ev({{.*}}[[ALLOC_RESULT]])
+// CHECK-NEXT: unwind label %[[S5LPAD:.*]]
+// CHECK: @_ZN2S5dlEPS_St19destroying_delete_t(
+// CHECK: [[S5LPAD]]:
+// CHECK: @_ZN2S5dlIS_EEvSt13type_identityIT_EPv({{.*}}, {{.*}} [[ALLOC_RESULT]])
+
+struct S6 {
+  S6();
+  virtual ~S6();
+  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, std::align_val_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*, std::align_val_t);
+};
+
+struct __attribute__((aligned(128))) S7 : S6 {
+
+};
+
+struct S8 : S6 {
+  S8();
+  void *operator new(size_t);
+  void operator delete(void*);
+};
+
+extern "C" void test_ensure_type_aware_overrides() {
+  S6 *s6 = new S6;
+  delete s6;
+  S7 *s7 = new S7;
+  delete s7;
+  S8 *s8 = new S8;
+  delete s8;
+}
+
+// CHECK-LABEL: test_ensure_type_aware_overrides
+// CHECK: [[S6_ALLOC:%.*]] = call {{.*}} @_ZN2S6nwIS_EEPvSt13type_identityIT_Em(
+// CHECK: @_ZN2S6C1Ev({{.*}}[[S6_ALLOC]])
+// CHECK-NEXT: unwind label %[[S6LPAD:.*]]
+// CHECK: [[S6_VTABLE:%vtable.*]] = load 
+// CHECK: [[S6_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S6_VTABLE]], i64 1
+// CHECK: [[S6_DFN:%.*]] = load ptr, ptr [[S6_DFN_ADDR]]
+// CHECK: call void [[S6_DFN]](
+// CHECK: [[S7_ALLOC:%.*]] = call {{.*}} @_ZN2S6nwI2S7EEPvSt13type_identityIT_EmSt11align_val_t(
+// CHECK: @_ZN2S7C1Ev({{.*}}[[S7_ALLOC]])
+// CHECK-NEXT: unwind label %[[S7LPAD:.*]]
+// CHECK: [[S7_VTABLE:%vtable.*]] = load
+// CHECK: [[S7_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S7_VTABLE]], i64 1
+// CHECK: [[S7_DFN:%.*]] = load ptr, ptr [[S7_DFN_ADDR]]
+// CHECK: call void [[S7_DFN]](
+// CHECK: [[S8_ALLOC:%.*]] = call {{.*}} @_ZN2S8nwEm(
+// CHECK: @_ZN2S8C1Ev({{.*}}[[S8_ALLOC]])
+// CHECK-NEXT: unwind label %[[S8LPAD:.*]]
+// CHECK: [[S8_VTABLE:%vtable.*]] = load
+// CHECK: [[S8_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S8_VTABLE]], i64 1
+// CHECK: [[S8_DFN:%.*]] = load ptr, ptr [[S8_DFN_ADDR]]
+// CHECK: call void [[S8_DFN]](
+// CHECK: [[S6LPAD]]:
+// CHECK: @_ZN2S6dlIS_EEvSt13type_identityIT_EPv({{.*}}, {{.*}} [[S6_ALLOC]])
+// CHECK: [[S7LPAD]]:
+// CHECK: @_ZN2S6dlI2S7EEvSt13type_identityIT_EPvSt11align_val_t({{.*}}, {{.*}} [[S7_ALLOC]], {{.*}})
+// CHECK: [[S8LPAD]]:
+// CHECK: @_ZN2S8dlEPv({{.*}} [[S8_ALLOC]])
+
+
+struct S9 {
+  S9();
+  template <typename T> void *operator new(std::type_identity<T>, size_t);
+  void operator delete(void*);
+};
+
+struct S10 {
+  S10();
+  void *operator new(size_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*);
+};
+
+
+extern "C" void test_mismatched_operators() {
+  S9* s9 = new S9;
+  delete s9;
+  S10 *s10 = new S10;
+  delete s10;
+}
+
+// CHECK-LABEL: test_mismatched_operators
+// CHECK: [[S9_ALLOC:%.*]] = call {{.*}} @_ZN2S9nwIS_EEPvSt13type_identityIT_Em({{.*}}, {{.*}})
+// CHECK: @_ZN2S9C1Ev({{.*}} [[S9_ALLOC]])
+// CHECK-NEXT: unwind label %[[S9LPAD:.*]]
+// CHECK: @_ZN2S9dlEPv(
+// CHECK: [[S10_ALLOC:%.*]] = call {{.*}} @_ZN3S10nwEm(
+// CHECK: @_ZN3S10C1Ev({{.*}}[[S10_ALLOC]])
+// CHECK-NEXT: unwind label %[[S10LPAD:.*]]
+// CHECK: call void @_ZN3S10dlIS_EEvSt13type_identityIT_EPv({{.*}}, {{.*}})
+// CHECK: [[S9LPAD]]:
+// CHECK: @_ZN2S9dlEPv({{.*}}[[S9_ALLOC]])
+// CHECK: [[S10LPAD]]:
+// CHECK: @_ZN3S10dlIS_EEvSt13type_identityIT_EPv({{.*}}, {{.*}}[[S10_ALLOC]])
+
+struct __attribute__((aligned(128))) S11 {
+  S11();
+  template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+  template <typename T> void operator delete(std::type_identity<T>, void*);
+  void operator delete(S11*, std::destroying_delete_t, std::align_val_t);
+};
+
+// void test_unaligned_cleanup() {
+//   S11 *s11 = new S11;
+//   delete s11;
+// }
diff --git a/clang/test/SemaCXX/type-aware-placement-operators.cpp b/clang/test/SemaCXX/type-aware-placement-operators.cpp
new file mode 100644
index 00000000000000..96cb27d31f7bd5
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-placement-operators.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s        -std=c++23 -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; };
+}
+
+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__;
+struct Context;
+struct S1 {
+  S1() throw();
+};
+void *operator new(std::type_identity<S1>, size_t, Context&);
+void operator delete(std::type_identity<S1>, void*, Context&) = delete; // #1
+
+struct S2 {
+  S2() throw();
+  template<typename T> void *operator new(std::type_identity<T>, size_t, Context&);
+  template<typename T> void operator delete(std::type_identity<T>, void*, Context&) = delete; // #2
+};
+
+void test(Context& Ctx) {
+  S1 *s1 = new (Ctx) S1;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#1 {{'operator delete' has been explicitly marked deleted here}}
+  delete s1;
+  S2 *s2 = new (Ctx) S2;
+  // expected-error at -1 {{attempt to use a deleted function}}
+  // expected-note@#2 {{'operator delete<S2>' has been explicitly marked deleted here}}
+  delete s2;
+  // expected-error at -1 {{no suitable member 'operator delete' in 'S2'}}
+  // expected-note@#2 {{member 'operator delete' declared here}}
+}



More information about the cfe-commits mailing list