[clang] [RFC] Initial implementation of P2719 (PR #113510)
Oliver Hunt via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 4 21:54:00 PST 2024
https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/113510
>From 9730efc08b52431ee650ea0abf88b998e5057c8d Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 19 Oct 2024 18:46:23 -0700
Subject: [PATCH 1/2] 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 7ff35d73df59973..5235ace380bd9ee 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 57ab94bcb2010f3..3433966a5565946 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 83fafbabb1d460c..8717b2b3e848462 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 1bcc7ee0b70dee9..b7ee0f2f5cf046d 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 72eada50a56cc9e..7fa46023593466e 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 d697e6d61afa9a2..1a21ed3f8da14eb 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 7f5d26118bdc714..a9ff58fb94ebc61 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 39e4851dd3814c8..ed9cb53727d6120 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 805b79491e6ea44..3c7b41a38710cf0 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 93d98e1cbb9c811..90bc45a5567faad 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 4552df2a2a31e66..f6a43bf5f493b73 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 9d0b77566f6747d..e542e7399129495 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 cd173d17263792c..c0d0336e1db4a37 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 1c92fd9e3ff0676..db3d884ec3ec500 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 a2c0c60d43dd14c..7a9de7b9abd3b94 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 6bf2908e667c078..560b42e194a08c5 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 648b9b9ed98063d..fa4695e0e4f3821 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 dca8d3fd7b3eafd..3ad2ec16688fb9e 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 3724aaf804c9059..595bdd27eea3148 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 1aa3e8edfe1b13b..1a7d45256559a1c 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 1a691c0e1689d67..8c48ecf92eda480 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 50c1b24fce6da7d..ad32debe0d24eba 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 4503e60cff8c2f0..ba9edefc44a048a 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 6aaafb2e8d71ccc..15a85b6f0b83363 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 321e0031661ee26..e7372b51f397c38 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 af225529c494e62..a0eff4b612980cc 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 9c60c32a5c5440d..b0a56597a3a7bd6 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 7b3be7e0b7f98d4..2409610e519a2ef 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 585a242b22474e2..47b4a06fc97ed6a 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 262c163fb17897b..c084ce64a59b1cb 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 c9797208e2dc584..2a898eef06dfaab 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 7d1f51cb218ceb2..787615e52fb0130 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 000000000000000..962d03c6839adb5
--- /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 000000000000000..d46bc7bd845c601
--- /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 000000000000000..e0dc0612a16defc
--- /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 000000000000000..a80a7cc5a9f3a60
--- /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 000000000000000..62f1672fd90b327
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -0,0 +1,302 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++2c -fexperimental-cxx-type-aware-allocators -fexceptions
+// RUN: %clang_cc1 -fsyntax-only -verify %s -DTADD -std=c++2c -fexperimental-cxx-type-aware-allocators -fcxx-type-aware-destroying-delete -fexceptions
+
+namespace std {
+ template <class T> struct type_identity {};
+ enum class align_val_t : __SIZE_TYPE__ {};
+ struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+
+static_assert(__has_feature(cxx_type_aware_allocators));
+#ifdef TADD
+static_assert(__has_feature(cxx_type_aware_destroying_delete));
+#else
+static_assert(!__has_feature(cxx_type_aware_destroying_delete));
+#endif
+
+using size_t = __SIZE_TYPE__;
+
+void *operator new(size_t);
+void *operator new(size_t, std::align_val_t);
+void operator delete(void *);
+
+struct UntypedInclassNew {
+ void *operator new(size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+ void operator delete(void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<UntypedInclassNew>, size_t); // expected-note {{candidate function not viable}}
+void operator delete(std::type_identity<UntypedInclassNew>, void*);
+
+struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_NoAlignedAlloc {
+ void *operator new(size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+ void operator delete(void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, size_t, std::align_val_t); // expected-note {{candidate function not viable}}
+void operator delete(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, void *, std::align_val_t);
+
+struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_AlignedAlloc {
+ void *operator new(size_t, std::align_val_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+ void operator delete(void *, std::align_val_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, size_t, std::align_val_t); // expected-note {{candidate function not viable}}
+void operator delete(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, void *, std::align_val_t);
+
+struct BasicClass {};
+void *operator new(std::type_identity<BasicClass>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+void operator delete(std::type_identity<BasicClass>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+
+struct InclassNew1 {
+ void *operator new(std::type_identity<InclassNew1>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+ void operator delete(std::type_identity<InclassNew1>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<InclassNew1>, size_t); // expected-note {{candidate function not viable}}
+void operator delete(std::type_identity<InclassNew1>, void *);
+
+struct InclassNew2 {
+ template <typename T> void *operator new(std::type_identity<T>, size_t) = delete; // expected-note {{candidate function [with T = InclassNew2] has been explicitly deleted}}
+ template <typename T> void operator delete(std::type_identity<T>, void *) = delete; // expected-note {{'operator delete<InclassNew2>' has been explicitly marked deleted here}}
+};
+void *operator new(std::type_identity<InclassNew2>, size_t); // expected-note {{candidate function not viable}}
+void operator delete(std::type_identity<InclassNew2>, void *);
+
+struct InclassNew3 {
+ void *operator new(std::type_identity<InclassNew3>, size_t) = delete; // expected-note {{candidate function has been explicitly deleted}}
+ void operator delete(std::type_identity<InclassNew3>, void *) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+ template <typename T> void *operator new(std::type_identity<T>, size_t); // expected-note {{candidate function [with T = InclassNew3]}}
+ template <typename T> void operator delete(std::type_identity<T>, void *);
+};
+
+struct __attribute__((aligned(128))) InclassNew4 {
+ void *operator new(std::type_identity<InclassNew4>, size_t); // expected-note {{candidate function not viable}}
+ void operator delete(std::type_identity<InclassNew4>, void *);
+ template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // expected-note {{candidate function [with T = InclassNew4] has been explicitly deleted}}
+ template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t) = delete; // expected-note {{'operator delete<InclassNew4>' has been explicitly marked deleted here}}
+};
+
+struct InclassNew5 {
+ InclassNew5();
+ void *operator new(std::type_identity<InclassNew5>, size_t);
+ void operator delete(void *);
+ void operator delete(std::type_identity<InclassNew5>, void *) = delete; // expected-note 2 {{'operator delete' has been explicitly marked deleted here}}
+};
+
+struct InclassNew6 {
+ InclassNew6();
+ void *operator new(size_t); // expected-note {{non-type aware 'operator new' declared here}}
+ void operator delete(void *) = delete;
+ void operator delete(std::type_identity<InclassNew6>, void *) = delete; // expected-note 2 {{'operator delete' has been explicitly marked deleted here}}
+ // expected-note at -1 {{type aware 'operator delete' declared here}}
+};
+
+struct InclassNew7 {
+ InclassNew7();
+ void *operator new(std::type_identity<InclassNew7>, size_t);
+ void operator delete(std::type_identity<InclassNew7>, void *);
+ void operator delete(InclassNew7 *, std::destroying_delete_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+};
+
+struct InclassNew8 {
+ InclassNew8();
+ void *operator new(std::type_identity<InclassNew8>, size_t); // expected-note {{type aware 'operator new' declared here}}
+ void operator delete(void*); // expected-note {{non-type aware 'operator delete' declared here}}
+};
+
+struct InclassNew9 {
+ InclassNew9();
+ void *operator new(std::type_identity<InclassNew9>, size_t);
+ // expected-note at -1 {{type aware 'operator new' found in 'InclassNew9'}}
+};
+
+void operator delete(std::type_identity<InclassNew9>, void*);
+// expected-note at -1 {{type aware 'operator delete' found in the global namespace}}
+
+struct BaseClass1 {
+ template <typename T> void *operator new(std::type_identity<T>, size_t);
+ template <typename T> void operator delete(std::type_identity<T>, void*) = delete;
+ // expected-note at -1 2 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
+ virtual ~BaseClass1(); // expected-note {{overridden virtual function is here}}
+};
+
+struct SubClass1 : BaseClass1 {
+ // expected-error at -1 {{deleted function '~SubClass1' cannot override a non-deleted function}}
+ // expected-note at -2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+};
+
+struct BaseClass2 {
+ template <typename T> void *operator new(std::type_identity<T>, size_t);
+ template <typename T> void operator delete(std::type_identity<T>, void*) = delete;
+ // expected-note at -1 {{'operator delete<SubClass2>' has been explicitly marked deleted here}}
+ void operator delete(BaseClass2 *, std::destroying_delete_t);
+ virtual ~BaseClass2();
+};
+
+struct SubClass2 : BaseClass2 {
+ SubClass2(); // Force exception cleanup which should invoke type aware delete
+};
+
+struct BaseClass3 {
+ template <typename T> void *operator new(std::type_identity<T>, size_t);
+ template <typename T> void operator delete(std::type_identity<T>, void*);
+ void operator delete(BaseClass3 *, std::destroying_delete_t) = delete; // expected-note {{'operator delete' has been explicitly marked deleted here}}
+ virtual ~BaseClass3(); // expected-note {{overridden virtual function is here}}
+};
+struct SubClass3 : BaseClass3 {
+ // expected-error at -1 {{deleted function '~SubClass3' cannot override a non-deleted function}}
+ // expected-note at -2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+};
+
+template <typename A, typename B> concept Derived = requires (A * a, B *b) { a = b; };
+template <typename A, typename B> concept Same = requires (std::type_identity<A> * a, std::type_identity<B> *b) { a = b; };
+
+struct SubClass4;
+struct BaseClass4 {
+ template <Derived<SubClass4> T> void *operator new(std::type_identity<T>, size_t) = delete;
+ // expected-note at -1 {{candidate function [with T = SubClass4] has been explicitly deleted}}
+ template <Derived<SubClass4> T> void operator delete(std::type_identity<T>, void*) = delete;
+ // expected-note at -1 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
+ template <typename T> void *operator new(std::type_identity<T>, size_t);
+ // expected-note at -1 {{candidate function [with T = SubClass4]}}
+ template <typename T> void operator delete(std::type_identity<T>, void*);
+
+ virtual ~BaseClass4(); // expected-note {{overridden virtual function is here}}
+};
+
+struct SubClass4 : BaseClass4 {
+ // expected-error at -1 {{deleted function '~SubClass4' cannot override a non-deleted function}}
+ // expected-note at -2 2 {{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+};
+struct SubClass4_1 : SubClass4 {
+ // expected-note at -1 {{destructor of 'SubClass4_1' is implicitly deleted because base class 'SubClass4' has a deleted destructor}}
+ SubClass4_1();
+};
+struct SubClass4_2 : BaseClass4 {
+};
+
+struct SubClass5;
+struct BaseClass5 {
+ template <Same<SubClass5> T> void *operator new(std::type_identity<T>, size_t);
+ template <Same<SubClass5> T> void operator delete(std::type_identity<T>, void*); // expected-note {{member 'operator delete' declared here}}
+ template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void *operator new(std::type_identity<T>, size_t) = delete;
+ template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void operator delete(std::type_identity<T>, void*) = delete;
+ // expected-note at -1 {{member 'operator delete' declared here}}
+};
+
+struct SubClass5 : BaseClass5 {
+};
+struct SubClass5_1 : SubClass5 {
+};
+
+
+struct BaseClass6 {
+ template <typename T> void *operator new(std::type_identity<T>, size_t);
+ // expected-note at -1 {{type aware 'operator new' found in 'BaseClass6'}}
+ template <typename T> void operator delete(std::type_identity<T>, void*);
+ // expected-note at -1 {{type aware 'operator delete' found in 'BaseClass6'}}
+ BaseClass6();
+ virtual ~BaseClass6();
+};
+
+struct SubClass6_1 : BaseClass6 {
+ template <typename T> void *operator new(std::type_identity<T>, size_t);
+ // expected-note at -1 {{type aware 'operator new' found in 'SubClass6_1'}}
+ SubClass6_1();
+};
+struct SubClass6_2 : BaseClass6 {
+ template <typename T> void operator delete(std::type_identity<T>, void*);
+ // expected-note at -1 {{type aware 'operator delete' found in 'SubClass6_2'}}
+ SubClass6_2();
+};
+
+
+void test() {
+
+ // untyped in class declaration wins
+ UntypedInclassNew *O1 = new UntypedInclassNew; // expected-error {{call to deleted function 'operator new'}}
+ delete O1; // expected-error {{attempt to use a deleted function}}
+
+ // untyped in class declaration wins, even though global is aligned and in class is not
+ UntypedInclassNewOveraligned_NoAlignedAlloc *O2 = new UntypedInclassNewOveraligned_NoAlignedAlloc; // expected-error {{call to deleted function 'operator new'}}
+ delete O2; // expected-error {{attempt to use a deleted function}}
+
+ // untyped in class declaration wins
+ UntypedInclassNewOveraligned_AlignedAlloc *O3 = new UntypedInclassNewOveraligned_AlignedAlloc; // expected-error {{call to deleted function 'operator new'}}
+ delete O3; // expected-error {{attempt to use a deleted function}}
+
+ // We resolve the explicitly typed free operator
+ BasicClass *O4 = new BasicClass; // expected-error {{call to deleted function 'operator new'}}
+ delete O4; // expected-error {{attempt to use a deleted function}}
+
+ // We resolve the explicitly typed in class operator
+ InclassNew1 *O5 = new InclassNew1; // expected-error {{call to deleted function 'operator new'}}
+ delete O5; // expected-error {{attempt to use a deleted function}}
+
+ // We resolve the unconstrained in class operators over the constrained free operators
+ InclassNew2 *O6 = new InclassNew2; // expected-error {{call to deleted function 'operator new'}}
+ delete O6; // expected-error {{attempt to use a deleted function}}
+
+ // We prefer the constrained in class operators over the unconstrained variants
+ InclassNew3 *O7 = new InclassNew3; // expected-error {{call to deleted function 'operator new'}}
+ delete O7; // expected-error {{attempt to use a deleted function}}
+
+ // We prefer the aligned but unconstrained operators over the unaligned but constrained variants
+ InclassNew4 *O8 = new InclassNew4; // expected-error {{call to deleted function 'operator new'}}
+ delete O8; // expected-error {{attempt to use a deleted function}}
+
+ // Constructor clean up invokes typed operator if typed new was used
+ InclassNew5 *O9 = new InclassNew5; // expected-error {{attempt to use a deleted function}}
+ delete O9; // expected-error {{attempt to use a deleted function}}
+
+ // Are these reasonable? Should we ensure that declaration of new vs delete have consistent type
+ // semantics? How do we define consistent?
+ // Constructor clean up invokes untyped delete if untyped delete was used
+ InclassNew6 *O10 = new InclassNew6; // expected-error {{attempt to use a deleted function}}
+ // expected-warning at -1 {{mismatching type aware allocation operators for constructor cleanup}}
+ delete O10; //expected-error {{attempt to use a deleted function}}
+
+ // Destroying delete is prefered over typed delete
+ InclassNew7 *O11 = new InclassNew7;
+ delete O11; // expected-error {{attempt to use a deleted function}}
+
+ InclassNew8 *O12 = new InclassNew8;
+ // expected-warning at -1 {{mismatching type aware allocation operators for constructor cleanup}}
+ delete O12;
+
+ InclassNew9 *O13 = new InclassNew9;
+ // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'InclassNew9'}}
+ delete O13;
+
+ // Creating the virtual destructor for an type requires the deleting destructor
+ // for that type
+ SubClass1 *O14 = new SubClass1; // expected-error {{attempt to use a deleted function}}
+ delete O14; // expected-error {{attempt to use a deleted function}}
+
+ SubClass2 *O15 = new SubClass2; // expected-error {{attempt to use a deleted function}}
+ delete O15;
+
+ // Deletion triggers destroying delete despite type aware delete
+ SubClass3 *O16 = new SubClass3;
+ delete O16; // expected-error {{attempt to use a deleted function}}
+
+ SubClass4 *O17 = new SubClass4; // expected-error {{call to deleted function 'operator new'}}
+ delete O17; // expected-error {{attempt to use a deleted function}}
+
+ SubClass4_1 *O18 = new SubClass4_1;
+ delete O18; // expected-error {{attempt to use a deleted function}}
+
+ SubClass4_2 *O19 = new SubClass4_2;
+ delete O19;
+
+ SubClass5 *O20 = new SubClass5;
+ delete O20;
+
+ SubClass5_1 *O21 = new SubClass5_1; // expected-error {{no matching function for call to 'operator new'}}
+ delete O21; // expected-error {{no suitable member 'operator delete' in 'SubClass5_1'}}
+
+ SubClass6_1 *O22 = new SubClass6_1;
+ // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'SubClass6_1'}}
+ delete O22;
+
+ SubClass6_2 *O23 = new SubClass6_2;
+ // expected-error at -1 {{type aware 'operator new' requires matching 'operator delete' in 'BaseClass6'}}
+ delete O23;
+}
>From e721929ee8fae8f186631eb6e23eac08cc375b7c Mon Sep 17 00:00:00 2001
From: Oliver Hunt <github at nerget.com>
Date: Mon, 4 Nov 2024 21:53:51 -0800
Subject: [PATCH 2/2] 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 db3d884ec3ec500..b7f6a7449385f58 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;
More information about the cfe-commits
mailing list