Skip to content

Commit 9730efc

Browse files
committed
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.
1 parent ea85900 commit 9730efc

37 files changed

+1580
-287
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2530,6 +2530,8 @@ class FunctionDecl : public DeclaratorDecl,
25302530
/// Determine whether this is a destroying operator delete.
25312531
bool isDestroyingOperatorDelete() const;
25322532

2533+
bool IsTypeAwareOperatorNewOrDelete() const;
2534+
25332535
/// Compute the language linkage.
25342536
LanguageLinkage getLanguageLinkage() const;
25352537

clang/include/clang/AST/ExprCXX.h

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,6 +2234,17 @@ enum class CXXNewInitializationStyle {
22342234
Braces
22352235
};
22362236

2237+
struct ImplicitAllocationParameters {
2238+
bool PassTypeIdentity;
2239+
bool PassAlignment;
2240+
};
2241+
2242+
struct ImplicitDeallocationParameters {
2243+
bool PassTypeIdentity;
2244+
bool PassAlignment;
2245+
bool PassSize;
2246+
};
2247+
22372248
/// Represents a new-expression for memory allocation and constructor
22382249
/// calls, e.g: "new CXXNewExpr(foo)".
22392250
class CXXNewExpr final
@@ -2289,7 +2300,7 @@ class CXXNewExpr final
22892300

22902301
/// Build a c++ new expression.
22912302
CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
2292-
FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
2303+
FunctionDecl *OperatorDelete, ImplicitAllocationParameters IAP,
22932304
bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
22942305
SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
22952306
CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2304,7 +2315,7 @@ class CXXNewExpr final
23042315
/// Create a c++ new expression.
23052316
static CXXNewExpr *
23062317
Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew,
2307-
FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
2318+
FunctionDecl *OperatorDelete, ImplicitAllocationParameters IAP,
23082319
bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
23092320
SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
23102321
CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
@@ -2393,6 +2404,13 @@ class CXXNewExpr final
23932404
return const_cast<CXXNewExpr *>(this)->getPlacementArg(I);
23942405
}
23952406

2407+
unsigned getNumImplicitArgs() const {
2408+
unsigned ImplicitArgCount = 1; // Size
2409+
ImplicitArgCount += passAlignment();
2410+
ImplicitArgCount += passTypeIdentity();
2411+
return ImplicitArgCount;
2412+
}
2413+
23962414
bool isParenTypeId() const { return CXXNewExprBits.IsParenTypeId; }
23972415
SourceRange getTypeIdParens() const {
23982416
return isParenTypeId() ? getTrailingObjects<SourceRange>()[0]
@@ -2431,13 +2449,26 @@ class CXXNewExpr final
24312449
/// the allocation function.
24322450
bool passAlignment() const { return CXXNewExprBits.ShouldPassAlignment; }
24332451

2452+
/// Indicates whether a type_identity tag should be implicitly passed to
2453+
/// the allocation function.
2454+
bool passTypeIdentity() const {
2455+
return CXXNewExprBits.ShouldPassTypeIdentity;
2456+
}
2457+
24342458
/// Answers whether the usual array deallocation function for the
24352459
/// allocated type expects the size of the allocation as a
24362460
/// parameter.
24372461
bool doesUsualArrayDeleteWantSize() const {
24382462
return CXXNewExprBits.UsualArrayDeleteWantsSize;
24392463
}
24402464

2465+
/// Provides the full set of information about expected implicit
2466+
/// parameters in this call
2467+
ImplicitAllocationParameters implicitAllocationParameters() const {
2468+
return ImplicitAllocationParameters{.PassTypeIdentity = passTypeIdentity(),
2469+
.PassAlignment = passAlignment()};
2470+
}
2471+
24412472
using arg_iterator = ExprIterator;
24422473
using const_arg_iterator = ConstExprIterator;
24432474

clang/include/clang/AST/Stmt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,10 @@ class alignas(void *) Stmt {
890890
LLVM_PREFERRED_TYPE(bool)
891891
unsigned ShouldPassAlignment : 1;
892892

893+
/// Should the type identity be passed to the allocation function?
894+
LLVM_PREFERRED_TYPE(bool)
895+
unsigned ShouldPassTypeIdentity : 1;
896+
893897
/// If this is an array allocation, does the usual deallocation
894898
/// function for the allocated type want to know the allocated size?
895899
LLVM_PREFERRED_TYPE(bool)

clang/include/clang/AST/Type.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,6 +2636,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
26362636
// C++14 decltype(auto)
26372637
bool isTypedefNameType() const; // typedef or alias template
26382638

2639+
bool isTypeIdentitySpecialization() const; // std::type_identity<X> for any X
2640+
bool isDestroyingDeleteT() const; // std::destroying_delete_t
2641+
26392642
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
26402643
bool is##Id##Type() const;
26412644
#include "clang/Basic/OpenCLImageTypes.def"
@@ -2700,6 +2703,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
27002703
return static_cast<TypeDependence>(TypeBits.Dependence);
27012704
}
27022705

2706+
TemplateDecl *getSpecializedTemplateDecl() const;
2707+
27032708
/// Whether this type is an error type.
27042709
bool containsErrors() const {
27052710
return getDependence() & TypeDependence::Error;

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,6 +1524,10 @@ in addition with the pragmas or -fmax-tokens flag to get any warnings.
15241524
}];
15251525
}
15261526

1527+
// Warning group for type aware allocators
1528+
def TypeAwareAllocatorMismatch :
1529+
DiagGroup<"type-aware-allocator-mismatch">;
1530+
15271531
def WebAssemblyExceptionSpec : DiagGroup<"wasm-exception-spec">;
15281532

15291533
def RTTI : DiagGroup<"rtti">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9692,6 +9692,18 @@ def err_operator_delete_param_type : Error<
96929692
def err_destroying_operator_delete_not_usual : Error<
96939693
"destroying operator delete can have only an optional size and optional "
96949694
"alignment parameter">;
9695+
def err_type_aware_destroying_operator_delete : Error<
9696+
"type aware destroying delete is not permitted">;
9697+
def err_unsupported_type_aware_allocator : Error<
9698+
"type aware allocation operators are disabled">;
9699+
def err_no_matching_type_aware_cleanup_deallocator_mismatch : Error<
9700+
"type aware %0 requires matching %1 in %2">;
9701+
def err_type_aware_operator_found : Note<
9702+
"type aware %0 found in %1">;
9703+
def warn_mismatching_type_aware_cleanup_deallocator : Warning<
9704+
"mismatching type aware allocation operators for constructor cleanup">, InGroup<TypeAwareAllocatorMismatch>;
9705+
def note_type_aware_operator_declared : Note<
9706+
"%select{|non-}0type aware %1 declared here">;
96959707
def note_implicit_delete_this_in_destructor_here : Note<
96969708
"while checking implicit 'delete this' for virtual destructor">;
96979709
def err_builtin_operator_new_delete_not_usual : Error<

clang/include/clang/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,10 @@ EXTENSION(datasizeof, LangOpts.CPlusPlus)
307307

308308
FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables)
309309

310+
// Type aware allocators
311+
FEATURE(cxx_type_aware_allocators, LangOpts.TypeAwareAllocators)
312+
FEATURE(cxx_type_aware_destroying_delete, LangOpts.TypeAwareDestroyingDelete)
313+
310314
// CUDA/HIP Features
311315
FEATURE(cuda_noinline_keyword, LangOpts.CUDA)
312316
EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates)

clang/include/clang/Basic/LangOptions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ LANGOPT(OpenACC , 1, 0, "OpenACC Enabled")
312312

313313
LANGOPT(MSVCEnableStdcMacro , 1, 0, "Define __STDC__ with '-fms-compatibility'")
314314
LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
315+
LANGOPT(TypeAwareAllocators , 1, 1, "type aware C++ allocation operators")
316+
LANGOPT(TypeAwareDestroyingDelete , 1, 1, "type aware C++ destroying delete")
315317
LANGOPT(AlignedAllocation , 1, 0, "aligned allocation")
316318
LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable")
317319
LANGOPT(NewAlignOverride , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'")

clang/include/clang/Driver/Options.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3543,7 +3543,14 @@ def : Separate<["-"], "fnew-alignment">, Alias<fnew_alignment_EQ>;
35433543
def : Flag<["-"], "faligned-new">, Alias<faligned_allocation>;
35443544
def : Flag<["-"], "fno-aligned-new">, Alias<fno_aligned_allocation>;
35453545
def faligned_new_EQ : Joined<["-"], "faligned-new=">;
3546-
3546+
defm cxx_type_aware_allocators : BoolFOption<"experimental-cxx-type-aware-allocators",
3547+
LangOpts<"TypeAwareAllocators">, DefaultFalse,
3548+
PosFlag<SetTrue, [], [ClangOption], "Enable C++YY type aware allocator operators">,
3549+
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;
3550+
defm cxx_type_aware_destroying_delete : BoolFOption<"cxx-type-aware-destroying-delete",
3551+
LangOpts<"TypeAwareDestroyingDelete">, DefaultFalse,
3552+
PosFlag<SetTrue, [], [ClangOption], "Enable C++YY type aware allocator operators">,
3553+
NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>;
35473554
def fobjc_legacy_dispatch : Flag<["-"], "fobjc-legacy-dispatch">, Group<f_Group>;
35483555
def fobjc_new_property : Flag<["-"], "fobjc-new-property">, Group<clang_ignored_f_Group>;
35493556
defm objc_infer_related_result_type : BoolFOption<"objc-infer-related-result-type",

clang/include/clang/Sema/Sema.h

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,10 @@ class Sema final : public SemaBase {
21352135
isConstantEvaluatedOverride;
21362136
}
21372137

2138+
bool AllowTypeAwareAllocators() const {
2139+
return getLangOpts().TypeAwareAllocators && !isConstantEvaluatedContext();
2140+
}
2141+
21382142
SourceLocation getLocationOfStringLiteralByte(const StringLiteral *SL,
21392143
unsigned ByteNo) const;
21402144

@@ -4751,6 +4755,15 @@ class Sema final : public SemaBase {
47514755

47524756
CXXRecordDecl *getStdBadAlloc() const;
47534757
EnumDecl *getStdAlignValT() const;
4758+
ClassTemplateDecl *getStdTypeIdentity() const;
4759+
std::optional<QualType> InstantiateSpecializedTypeIdentity(QualType Subject);
4760+
bool IsTypeIdentitySpecialization(QualType Type) const;
4761+
bool IsTypeAwareOperatorNewOrDelete(const FunctionDecl *FnDecl) const;
4762+
bool IsTypeAwareOperatorNewOrDelete(const FunctionTemplateDecl *FnDecl) const;
4763+
bool IsTypeAwareOperatorNewOrDelete(const NamedDecl *FnDecl) const;
4764+
std::optional<FunctionDecl *>
4765+
InstantiateTypeAwareUsualDelete(FunctionTemplateDecl *FnDecl,
4766+
QualType AllocType);
47544767

47554768
ValueDecl *tryLookupUnambiguousFieldDecl(RecordDecl *ClassDecl,
47564769
const IdentifierInfo *MemberOrBase);
@@ -7934,6 +7947,10 @@ class Sema final : public SemaBase {
79347947
/// The C++ "type_info" declaration, which is defined in \<typeinfo>.
79357948
RecordDecl *CXXTypeInfoDecl;
79367949

7950+
/// The C++ "std::type_identity" template class, which is defined by the C++
7951+
/// standard library.
7952+
LazyDeclPtr StdTypeIdentity;
7953+
79377954
/// A flag to remember whether the implicit forms of operator new and delete
79387955
/// have been declared.
79397956
bool GlobalNewDeleteDeclared;
@@ -8127,7 +8144,7 @@ class Sema final : public SemaBase {
81278144

81288145
/// The scope in which to find allocation functions.
81298146
enum AllocationFunctionScope {
8130-
/// Only look for allocation functions in the global scope.
8147+
/// Only look for allocation functions in the global scope
81318148
AFS_Global,
81328149
/// Only look for allocation functions in the scope of the
81338150
/// allocated class.
@@ -8139,14 +8156,12 @@ class Sema final : public SemaBase {
81398156

81408157
/// Finds the overloads of operator new and delete that are appropriate
81418158
/// for the allocation.
8142-
bool FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
8143-
AllocationFunctionScope NewScope,
8144-
AllocationFunctionScope DeleteScope,
8145-
QualType AllocType, bool IsArray,
8146-
bool &PassAlignment, MultiExprArg PlaceArgs,
8147-
FunctionDecl *&OperatorNew,
8148-
FunctionDecl *&OperatorDelete,
8149-
bool Diagnose = true);
8159+
bool FindAllocationFunctions(
8160+
SourceLocation StartLoc, SourceRange Range,
8161+
AllocationFunctionScope NewScope, AllocationFunctionScope DeleteScope,
8162+
QualType AllocType, bool IsArray, ImplicitAllocationParameters &IAP,
8163+
MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew,
8164+
FunctionDecl *&OperatorDelete, bool Diagnose = true);
81508165

81518166
/// DeclareGlobalNewDelete - Declare the global forms of operator new and
81528167
/// delete. These are:
@@ -8177,11 +8192,11 @@ class Sema final : public SemaBase {
81778192

81788193
bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
81798194
DeclarationName Name, FunctionDecl *&Operator,
8180-
bool Diagnose = true, bool WantSize = false,
8181-
bool WantAligned = false);
8182-
FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
8183-
bool CanProvideSize,
8184-
bool Overaligned,
8195+
QualType DeallocType,
8196+
ImplicitDeallocationParameters,
8197+
bool Diagnose = true);
8198+
FunctionDecl *FindUsualDeallocationFunction(QualType, SourceLocation StartLoc,
8199+
ImplicitDeallocationParameters,
81858200
DeclarationName Name);
81868201
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
81878202
CXXRecordDecl *RD);

0 commit comments

Comments
 (0)