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

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 24 20:47:55 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-static-analyzer-1

@llvm/pr-subscribers-clang-modules

Author: Oliver Hunt (ojhunt)

<details>
<summary>Changes</summary>

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 `-fcxx-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, `-fcxx-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.

---

Patch is 138.06 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/113510.diff


37 Files Affected:

- (modified) clang/include/clang/AST/Decl.h (+2) 
- (modified) clang/include/clang/AST/ExprCXX.h (+33-2) 
- (modified) clang/include/clang/AST/Stmt.h (+4) 
- (modified) clang/include/clang/AST/Type.h (+5) 
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+4) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+12) 
- (modified) clang/include/clang/Basic/Features.def (+4) 
- (modified) clang/include/clang/Basic/LangOptions.def (+2) 
- (modified) clang/include/clang/Driver/Options.td (+8-1) 
- (modified) clang/include/clang/Sema/Sema.h (+29-14) 
- (modified) clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h (+1-1) 
- (modified) clang/lib/AST/ASTImporter.cpp (+4-4) 
- (modified) clang/lib/AST/Decl.cpp (+34-3) 
- (modified) clang/lib/AST/DeclCXX.cpp (+30-3) 
- (modified) clang/lib/AST/ExprCXX.cpp (+9-8) 
- (modified) clang/lib/AST/Type.cpp (+23) 
- (modified) clang/lib/CodeGen/CGExprCXX.cpp (+51-19) 
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+8) 
- (modified) clang/lib/Sema/SemaCoroutine.cpp (+56-31) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+13) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+213-56) 
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+386-144) 
- (modified) clang/lib/Sema/SemaTemplate.cpp (+12) 
- (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+1) 
- (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+1) 
- (modified) clang/test/CodeGenCXX/new.cpp (+1) 
- (modified) clang/test/CodeGenCoroutines/coro-alloc-2.cpp (+2) 
- (modified) clang/test/CodeGenCoroutines/coro-alloc.cpp (+3) 
- (modified) clang/test/Modules/new-delete.cpp (+1) 
- (modified) clang/test/SemaCXX/coroutine-alloc-4.cpp (+28-1) 
- (modified) clang/test/SemaCXX/coroutine-allocs.cpp (+12) 
- (modified) clang/test/SemaCXX/delete.cpp (+5) 
- (added) clang/test/SemaCXX/type-aware-new-constexpr.cpp (+73) 
- (added) clang/test/SemaCXX/type-aware-new-delete-arrays.cpp (+49) 
- (added) clang/test/SemaCXX/type-aware-new-delete-basic-free-declarations.cpp (+95) 
- (added) clang/test/SemaCXX/type-aware-new-delete-basic-in-class-declarations.cpp (+64) 
- (added) clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp (+302) 


``````````diff
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 cfe3938f83847b..65696ee57822ed 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 ba3161c366f4d9..8af03e2afdd455 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2635,6 +2635,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"
@@ -2699,6 +2702,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 8e4718008ece72..90fad76c4af112 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9688,6 +9688,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 68db400c22e6c1..768e8a683babd0 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -313,6 +313,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 2ddb2f5312148e..c88fb6c948c54a 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3533,7 +3533,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<"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 9e6b04bc3f8f7c..05d416cdf7b3fd 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;
 
@@ -4749,6 +4753,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);
@@ -7933,6 +7946,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;
@@ -8126,7 +8143,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.
@@ -8138,14 +8155,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:
@@ -8176,11 +8191,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 549c864dc91ef2..c1f0f87ec0aeba 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -1141,7 +1141,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 e7a6509167f0a0..5bddd0741b17dc 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8313,10 +8313,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 8321cee0e0bc94..08618f6b38d3cd 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 17ebee0d6b22fa..5d88f92fc89379 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)-...
[truncated]

``````````

</details>


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


More information about the cfe-commits mailing list