mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-15 22:16:30 +00:00
[RFC] Initial implementation of P2719 (#113510)
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, std::align_val_t); void operator delete(std::type_identity<int>, void*, size_t, std::align_val_t); However this becomes more powerful by specifying templated declarations, for example template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);); 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.
This commit is contained in:
parent
2f29829475
commit
1cd59264aa
@ -93,6 +93,8 @@ C++2c Feature Support
|
||||
|
||||
- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
|
||||
|
||||
- Implemented `P2719R4 Type-aware allocation and deallocation functions <https://wg21.link/P2719>`_.
|
||||
|
||||
C++23 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -329,6 +329,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
/// This is lazily created. This is intentionally not serialized.
|
||||
mutable llvm::StringMap<StringLiteral *> StringLiteralCache;
|
||||
|
||||
mutable llvm::DenseSet<const FunctionDecl *> DestroyingOperatorDeletes;
|
||||
mutable llvm::DenseSet<const FunctionDecl *> TypeAwareOperatorNewAndDeletes;
|
||||
|
||||
/// The next string literal "version" to allocate during constant evaluation.
|
||||
/// This is used to distinguish between repeated evaluations of the same
|
||||
/// string literal.
|
||||
@ -3356,6 +3359,15 @@ public:
|
||||
void setStaticLocalNumber(const VarDecl *VD, unsigned Number);
|
||||
unsigned getStaticLocalNumber(const VarDecl *VD) const;
|
||||
|
||||
bool hasSeenTypeAwareOperatorNewOrDelete() const {
|
||||
return !TypeAwareOperatorNewAndDeletes.empty();
|
||||
}
|
||||
void setIsDestroyingOperatorDelete(const FunctionDecl *FD, bool IsDestroying);
|
||||
bool isDestroyingOperatorDelete(const FunctionDecl *FD) const;
|
||||
void setIsTypeAwareOperatorNewOrDelete(const FunctionDecl *FD,
|
||||
bool IsTypeAware);
|
||||
bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;
|
||||
|
||||
/// Retrieve the context for computing mangling numbers in the given
|
||||
/// DeclContext.
|
||||
MangleNumberingContext &getManglingNumberContext(const DeclContext *DC);
|
||||
|
@ -2540,6 +2540,38 @@ public:
|
||||
/// If this function is an allocation/deallocation function that takes
|
||||
/// the `std::nothrow_t` tag, return true through IsNothrow,
|
||||
bool isReplaceableGlobalAllocationFunction(
|
||||
UnsignedOrNone *AlignmentParam = nullptr,
|
||||
bool *IsNothrow = nullptr) const {
|
||||
if (isTypeAwareOperatorNewOrDelete())
|
||||
return false;
|
||||
return isUsableAsGlobalAllocationFunctionInConstantEvaluation(
|
||||
AlignmentParam, IsNothrow);
|
||||
}
|
||||
|
||||
/// Determines whether this function is one of the replaceable global
|
||||
/// allocation functions described in isReplaceableGlobalAllocationFunction,
|
||||
/// or is a function that may be treated as such during constant evaluation.
|
||||
/// This adds support for potentially templated type aware global allocation
|
||||
/// functions of the form:
|
||||
/// void *operator new(type-identity, std::size_t, std::align_val_t)
|
||||
/// void *operator new(type-identity, std::size_t, std::align_val_t,
|
||||
/// const std::nothrow_t &) noexcept;
|
||||
/// void *operator new[](type-identity, std::size_t, std::align_val_t)
|
||||
/// void *operator new[](type-identity, std::size_t, std::align_val_t,
|
||||
/// const std::nothrow_t &) noexcept;
|
||||
/// void operator delete(type-identity, void*, std::size_t,
|
||||
/// std::align_val_t) noexcept;
|
||||
/// void operator delete(type-identity, void*, std::size_t,
|
||||
/// std::align_val_t, const std::nothrow_t&) noexcept;
|
||||
/// void operator delete[](type-identity, void*, std::size_t,
|
||||
/// std::align_val_t) noexcept;
|
||||
/// void operator delete[](type-identity, void*, std::size_t,
|
||||
/// std::align_val_t, const std::nothrow_t&) noexcept;
|
||||
/// Where `type-identity` is a specialization of std::type_identity. If the
|
||||
/// declaration is a templated function, it may not include a parameter pack
|
||||
/// in the argument list, the type-identity parameter is required to be
|
||||
/// dependent, and is the only permitted dependent parameter.
|
||||
bool isUsableAsGlobalAllocationFunctionInConstantEvaluation(
|
||||
UnsignedOrNone *AlignmentParam = nullptr,
|
||||
bool *IsNothrow = nullptr) const;
|
||||
|
||||
@ -2548,6 +2580,20 @@ public:
|
||||
|
||||
/// Determine whether this is a destroying operator delete.
|
||||
bool isDestroyingOperatorDelete() const;
|
||||
void setIsDestroyingOperatorDelete(bool IsDestroyingDelete);
|
||||
|
||||
/// Count of mandatory parameters for type aware operator new
|
||||
static constexpr unsigned RequiredTypeAwareNewParameterCount =
|
||||
/* type-identity */ 1 + /* size */ 1 + /* alignment */ 1;
|
||||
|
||||
/// Count of mandatory parameters for type aware operator delete
|
||||
static constexpr unsigned RequiredTypeAwareDeleteParameterCount =
|
||||
/* type-identity */ 1 + /* address */ 1 + /* size */ 1 +
|
||||
/* alignment */ 1;
|
||||
|
||||
/// Determine whether this is a type aware operator new or delete.
|
||||
bool isTypeAwareOperatorNewOrDelete() const;
|
||||
void setIsTypeAwareOperatorNewOrDelete(bool IsTypeAwareOperator = true);
|
||||
|
||||
/// Compute the language linkage.
|
||||
LanguageLinkage getLanguageLinkage() const;
|
||||
|
@ -477,6 +477,34 @@ public:
|
||||
return OO_None;
|
||||
}
|
||||
|
||||
bool isAnyOperatorNew() const {
|
||||
if (getNameKind() != DeclarationName::CXXOperatorName)
|
||||
return false;
|
||||
switch (getCXXOverloadedOperator()) {
|
||||
case OO_New:
|
||||
case OO_Array_New:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isAnyOperatorDelete() const {
|
||||
if (getNameKind() != DeclarationName::CXXOperatorName)
|
||||
return false;
|
||||
switch (getCXXOverloadedOperator()) {
|
||||
case OO_Delete:
|
||||
case OO_Array_Delete:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isAnyOperatorNewOrDelete() const {
|
||||
return isAnyOperatorNew() || isAnyOperatorDelete();
|
||||
}
|
||||
|
||||
/// If this name is the name of a literal operator,
|
||||
/// retrieve the identifier associated with it.
|
||||
const IdentifierInfo *getCXXLiteralIdentifier() const {
|
||||
|
@ -2235,6 +2235,98 @@ enum class CXXNewInitializationStyle {
|
||||
Braces
|
||||
};
|
||||
|
||||
enum class TypeAwareAllocationMode : unsigned { No, Yes };
|
||||
|
||||
inline bool isTypeAwareAllocation(TypeAwareAllocationMode Mode) {
|
||||
return Mode == TypeAwareAllocationMode::Yes;
|
||||
}
|
||||
|
||||
inline TypeAwareAllocationMode
|
||||
typeAwareAllocationModeFromBool(bool IsTypeAwareAllocation) {
|
||||
return IsTypeAwareAllocation ? TypeAwareAllocationMode::Yes
|
||||
: TypeAwareAllocationMode::No;
|
||||
}
|
||||
|
||||
enum class AlignedAllocationMode : unsigned { No, Yes };
|
||||
|
||||
inline bool isAlignedAllocation(AlignedAllocationMode Mode) {
|
||||
return Mode == AlignedAllocationMode::Yes;
|
||||
}
|
||||
|
||||
inline AlignedAllocationMode alignedAllocationModeFromBool(bool IsAligned) {
|
||||
return IsAligned ? AlignedAllocationMode::Yes : AlignedAllocationMode::No;
|
||||
}
|
||||
|
||||
enum class SizedDeallocationMode : unsigned { No, Yes };
|
||||
|
||||
inline bool isSizedDeallocation(SizedDeallocationMode Mode) {
|
||||
return Mode == SizedDeallocationMode::Yes;
|
||||
}
|
||||
|
||||
inline SizedDeallocationMode sizedDeallocationModeFromBool(bool IsSized) {
|
||||
return IsSized ? SizedDeallocationMode::Yes : SizedDeallocationMode::No;
|
||||
}
|
||||
|
||||
struct ImplicitAllocationParameters {
|
||||
ImplicitAllocationParameters(QualType AllocType,
|
||||
TypeAwareAllocationMode PassTypeIdentity,
|
||||
AlignedAllocationMode PassAlignment)
|
||||
: Type(AllocType), PassTypeIdentity(PassTypeIdentity),
|
||||
PassAlignment(PassAlignment) {
|
||||
if (!Type.isNull())
|
||||
Type = Type.getUnqualifiedType();
|
||||
}
|
||||
explicit ImplicitAllocationParameters(AlignedAllocationMode PassAlignment)
|
||||
: PassTypeIdentity(TypeAwareAllocationMode::No),
|
||||
PassAlignment(PassAlignment) {}
|
||||
|
||||
unsigned getNumImplicitArgs() const {
|
||||
unsigned Count = 1; // Size
|
||||
if (isTypeAwareAllocation(PassTypeIdentity))
|
||||
++Count;
|
||||
if (isAlignedAllocation(PassAlignment))
|
||||
++Count;
|
||||
return Count;
|
||||
}
|
||||
|
||||
QualType Type;
|
||||
TypeAwareAllocationMode PassTypeIdentity;
|
||||
AlignedAllocationMode PassAlignment;
|
||||
};
|
||||
|
||||
struct ImplicitDeallocationParameters {
|
||||
ImplicitDeallocationParameters(QualType DeallocType,
|
||||
TypeAwareAllocationMode PassTypeIdentity,
|
||||
AlignedAllocationMode PassAlignment,
|
||||
SizedDeallocationMode PassSize)
|
||||
: Type(DeallocType), PassTypeIdentity(PassTypeIdentity),
|
||||
PassAlignment(PassAlignment), PassSize(PassSize) {
|
||||
if (!Type.isNull())
|
||||
Type = Type.getUnqualifiedType();
|
||||
}
|
||||
|
||||
ImplicitDeallocationParameters(AlignedAllocationMode PassAlignment,
|
||||
SizedDeallocationMode PassSize)
|
||||
: PassTypeIdentity(TypeAwareAllocationMode::No),
|
||||
PassAlignment(PassAlignment), PassSize(PassSize) {}
|
||||
|
||||
unsigned getNumImplicitArgs() const {
|
||||
unsigned Count = 1; // Size
|
||||
if (isTypeAwareAllocation(PassTypeIdentity))
|
||||
++Count;
|
||||
if (isAlignedAllocation(PassAlignment))
|
||||
++Count;
|
||||
if (isSizedDeallocation(PassSize))
|
||||
++Count;
|
||||
return Count;
|
||||
}
|
||||
|
||||
QualType Type;
|
||||
TypeAwareAllocationMode PassTypeIdentity;
|
||||
AlignedAllocationMode PassAlignment;
|
||||
SizedDeallocationMode PassSize;
|
||||
};
|
||||
|
||||
/// Represents a new-expression for memory allocation and constructor
|
||||
/// calls, e.g: "new CXXNewExpr(foo)".
|
||||
class CXXNewExpr final
|
||||
@ -2290,7 +2382,8 @@ class CXXNewExpr final
|
||||
|
||||
/// Build a c++ new expression.
|
||||
CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
|
||||
FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
|
||||
FunctionDecl *OperatorDelete,
|
||||
const ImplicitAllocationParameters &IAP,
|
||||
bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
|
||||
SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
|
||||
CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
|
||||
@ -2305,7 +2398,7 @@ public:
|
||||
/// Create a c++ new expression.
|
||||
static CXXNewExpr *
|
||||
Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew,
|
||||
FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
|
||||
FunctionDecl *OperatorDelete, const ImplicitAllocationParameters &IAP,
|
||||
bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
|
||||
SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
|
||||
CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
|
||||
@ -2394,6 +2487,10 @@ public:
|
||||
return const_cast<CXXNewExpr *>(this)->getPlacementArg(I);
|
||||
}
|
||||
|
||||
unsigned getNumImplicitArgs() const {
|
||||
return implicitAllocationParameters().getNumImplicitArgs();
|
||||
}
|
||||
|
||||
bool isParenTypeId() const { return CXXNewExprBits.IsParenTypeId; }
|
||||
SourceRange getTypeIdParens() const {
|
||||
return isParenTypeId() ? getTrailingObjects<SourceRange>()[0]
|
||||
@ -2439,6 +2536,15 @@ public:
|
||||
return CXXNewExprBits.UsualArrayDeleteWantsSize;
|
||||
}
|
||||
|
||||
/// Provides the full set of information about expected implicit
|
||||
/// parameters in this call
|
||||
ImplicitAllocationParameters implicitAllocationParameters() const {
|
||||
return ImplicitAllocationParameters{
|
||||
getAllocatedType(),
|
||||
typeAwareAllocationModeFromBool(CXXNewExprBits.ShouldPassTypeIdentity),
|
||||
alignedAllocationModeFromBool(CXXNewExprBits.ShouldPassAlignment)};
|
||||
}
|
||||
|
||||
using arg_iterator = ExprIterator;
|
||||
using const_arg_iterator = ConstExprIterator;
|
||||
|
||||
|
@ -891,6 +891,10 @@ protected:
|
||||
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)
|
||||
|
@ -64,8 +64,11 @@ def AlwaysInlineCoroutine :
|
||||
DiagGroup<"always-inline-coroutine">;
|
||||
def CoroNonAlignedAllocationFunction :
|
||||
DiagGroup<"coro-non-aligned-allocation-function">;
|
||||
def CoroTypeAwareAllocationFunction :
|
||||
DiagGroup<"coro-type-aware-allocation-function">;
|
||||
def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine,
|
||||
AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction]>;
|
||||
AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction,
|
||||
CoroTypeAwareAllocationFunction]>;
|
||||
def ObjCBoolConstantConversion : DiagGroup<"objc-bool-constant-conversion">;
|
||||
def ConstantConversion : DiagGroup<"constant-conversion",
|
||||
[BitFieldConstantConversion,
|
||||
|
@ -2595,8 +2595,8 @@ def err_auto_non_deduced_not_alone : Error<
|
||||
def err_implied_std_initializer_list_not_found : Error<
|
||||
"cannot deduce type of initializer list because std::initializer_list was "
|
||||
"not found; include <initializer_list>">;
|
||||
def err_malformed_std_initializer_list : Error<
|
||||
"std::initializer_list must be a class template with a single type parameter">;
|
||||
def err_malformed_std_class_template : Error<
|
||||
"std::%0 must be a class template with a single type parameter">;
|
||||
def err_auto_init_list_from_c : Error<
|
||||
"cannot use %select{'auto'|<ERROR>|'__auto_type'}0 with "
|
||||
"%select{initializer list|array}1 in C">;
|
||||
@ -9757,7 +9757,7 @@ def err_operator_new_delete_invalid_result_type : Error<
|
||||
def err_operator_new_delete_dependent_result_type : Error<
|
||||
"%0 cannot have a dependent return type; use %1 instead">;
|
||||
def err_operator_new_delete_too_few_parameters : Error<
|
||||
"%0 must have at least one parameter">;
|
||||
"%select{|type aware }0%select{|destroying }1%2 must have at least %select{|one|two|three|four|five}3 parameter%s3">;
|
||||
def err_operator_new_delete_template_too_few_parameters : Error<
|
||||
"%0 template must have at least two parameters">;
|
||||
def warn_operator_new_returns_null : Warning<
|
||||
@ -9765,19 +9765,38 @@ def warn_operator_new_returns_null : Warning<
|
||||
"%select{| or 'noexcept'}1">, InGroup<OperatorNewReturnsNull>;
|
||||
|
||||
def err_operator_new_dependent_param_type : Error<
|
||||
"%0 cannot take a dependent type as first parameter; "
|
||||
"use size_t (%1) instead">;
|
||||
"%select{|type aware }0%select{|destroying }1%2 cannot take a dependent type as its %ordinal3 parameter; "
|
||||
"use %5 (%4) instead">;
|
||||
def err_operator_new_param_type : Error<
|
||||
"%0 takes type size_t (%1) as first parameter">;
|
||||
"%select{|type aware }0%select{|destroying }1%2 takes type %5 (%4) as %ordinal3 parameter">;
|
||||
def err_operator_new_default_arg: Error<
|
||||
"parameter of %0 cannot have a default argument">;
|
||||
def err_operator_delete_dependent_param_type : Error<
|
||||
"%0 cannot take a dependent type as first parameter; use %1 instead">;
|
||||
"%select{|type aware }0%select{|destroying }1%2 cannot take a dependent type as its %ordinal3 parameter; "
|
||||
"use %4 instead">;
|
||||
def err_operator_delete_param_type : Error<
|
||||
"first parameter of %0 must have type %1">;
|
||||
"%ordinal3 parameter of%select{| type aware}0%select{| destroying}1 %2 must have type %4">;
|
||||
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<
|
||||
"destroying delete is not permitted to be type aware">;
|
||||
|
||||
def ext_cxx26_type_aware_allocators : ExtWarn<
|
||||
"type aware allocators are a C++2c extension">, InGroup<CXX26>;
|
||||
def warn_cxx26_type_aware_allocators : Warning<
|
||||
"type aware allocators are incompatible with C++ standards before C++2c">,
|
||||
DefaultIgnore, InGroup<CXXPre26Compat>;
|
||||
def err_type_aware_allocator_missing_matching_operator : Error<
|
||||
"declaration of type aware %0 in %1 must have matching type aware %2"
|
||||
>;
|
||||
def note_unmatched_type_aware_allocator_declared : Note<
|
||||
"unmatched type aware %0 declared here">;
|
||||
def err_mismatching_type_aware_cleanup_deallocator : Error<
|
||||
"type aware %0 requires a matching type aware %select{|placement }1%2 to be declared in the same scope">;
|
||||
def note_type_aware_operator_declared : Note<
|
||||
"%select{non-|}0type aware %1 declared here in %2">;
|
||||
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<
|
||||
@ -12149,6 +12168,9 @@ def warn_always_inline_coroutine : Warning<
|
||||
def err_coroutine_unusable_new : Error<
|
||||
"'operator new' provided by %0 is not usable with the function signature of %1"
|
||||
>;
|
||||
def note_coroutine_unusable_type_aware_allocators : Note<
|
||||
"type aware %0 will not be used for coroutine allocation"
|
||||
>;
|
||||
def err_coroutine_unfound_nothrow_new : Error <
|
||||
"unable to find %select{'::operator new(size_t, nothrow_t)'|"
|
||||
"'::operator new(size_t, align_val_t, nothrow_t)'}1 for %0"
|
||||
@ -12168,6 +12190,9 @@ def err_coroutine_return_type : Error<
|
||||
"function returns a type %0 marked with [[clang::coro_return_type]] but is neither a coroutine nor a coroutine wrapper; "
|
||||
"non-coroutines should be marked with [[clang::coro_wrapper]] to allow returning coroutine return type"
|
||||
>;
|
||||
def warn_coroutine_type_aware_allocator_ignored : Warning <
|
||||
"type aware %0 will not be used for coroutine allocation">,
|
||||
InGroup<CoroTypeAwareAllocationFunction>;
|
||||
} // end of coroutines issue category
|
||||
|
||||
let CategoryName = "Documentation Issue" in {
|
||||
|
@ -624,6 +624,7 @@ defvar cpp14 = LangOpts<"CPlusPlus14">;
|
||||
defvar cpp17 = LangOpts<"CPlusPlus17">;
|
||||
defvar cpp20 = LangOpts<"CPlusPlus20">;
|
||||
defvar cpp23 = LangOpts<"CPlusPlus23">;
|
||||
defvar cpp26 = LangOpts<"CPlusPlus26">;
|
||||
defvar c99 = LangOpts<"C99">;
|
||||
defvar c23 = LangOpts<"C23">;
|
||||
defvar lang_std = LangOpts<"LangStd">;
|
||||
|
@ -4891,6 +4891,10 @@ public:
|
||||
CXXRecordDecl *getStdBadAlloc() const;
|
||||
EnumDecl *getStdAlignValT() const;
|
||||
|
||||
TypeAwareAllocationMode ShouldUseTypeAwareOperatorNewOrDelete() const;
|
||||
FunctionDecl *BuildTypeAwareUsualDelete(FunctionTemplateDecl *FnDecl,
|
||||
QualType AllocType, SourceLocation);
|
||||
|
||||
ValueDecl *tryLookupUnambiguousFieldDecl(RecordDecl *ClassDecl,
|
||||
const IdentifierInfo *MemberOrBase);
|
||||
|
||||
@ -4918,12 +4922,27 @@ public:
|
||||
/// it is and Element is not NULL, assigns the element type to Element.
|
||||
bool isStdInitializerList(QualType Ty, QualType *Element);
|
||||
|
||||
/// Tests whether Ty is an instance of std::type_identity and, if
|
||||
/// it is and TypeArgument is not NULL, assigns the element type to Element.
|
||||
/// If MalformedDecl is not null, and type_identity was ruled out due to being
|
||||
/// incorrectly structured despite having the correct name, the faulty Decl
|
||||
/// will be assigned to MalformedDecl.
|
||||
bool isStdTypeIdentity(QualType Ty, QualType *TypeArgument,
|
||||
const Decl **MalformedDecl = nullptr);
|
||||
|
||||
/// Looks for the std::initializer_list template and instantiates it
|
||||
/// with Element, or emits an error if it's not found.
|
||||
///
|
||||
/// \returns The instantiated template, or null on error.
|
||||
QualType BuildStdInitializerList(QualType Element, SourceLocation Loc);
|
||||
|
||||
/// Looks for the std::type_identity template and instantiates it
|
||||
/// with Type, or returns a null type if type_identity has not been declared
|
||||
///
|
||||
/// \returns The instantiated template, or null if std::type_identity is not
|
||||
/// declared
|
||||
QualType tryBuildStdTypeIdentity(QualType Type, SourceLocation Loc);
|
||||
|
||||
/// Determine whether Ctor is an initializer-list constructor, as
|
||||
/// defined in [dcl.init.list]p2.
|
||||
bool isInitListConstructor(const FunctionDecl *Ctor);
|
||||
@ -6172,6 +6191,10 @@ public:
|
||||
/// \<initializer_list>.
|
||||
ClassTemplateDecl *StdInitializerList;
|
||||
|
||||
/// The C++ "std::type_identity" template, which is defined in
|
||||
/// \<type_traits>.
|
||||
ClassTemplateDecl *StdTypeIdentity;
|
||||
|
||||
// Contains the locations of the beginning of unparsed default
|
||||
// argument locations.
|
||||
llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
|
||||
@ -8298,14 +8321,12 @@ public:
|
||||
|
||||
/// 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:
|
||||
@ -8336,11 +8357,10 @@ public:
|
||||
|
||||
bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
||||
DeclarationName Name, FunctionDecl *&Operator,
|
||||
bool Diagnose = true, bool WantSize = false,
|
||||
bool WantAligned = false);
|
||||
ImplicitDeallocationParameters,
|
||||
bool Diagnose = true);
|
||||
FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
|
||||
bool CanProvideSize,
|
||||
bool Overaligned,
|
||||
ImplicitDeallocationParameters,
|
||||
DeclarationName Name);
|
||||
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
|
||||
CXXRecordDecl *RD,
|
||||
|
@ -1146,7 +1146,7 @@ public:
|
||||
/// 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 {
|
||||
|
@ -13104,6 +13104,32 @@ unsigned ASTContext::getStaticLocalNumber(const VarDecl *VD) const {
|
||||
return I != StaticLocalNumbers.end() ? I->second : 1;
|
||||
}
|
||||
|
||||
void ASTContext::setIsDestroyingOperatorDelete(const FunctionDecl *FD,
|
||||
bool IsDestroying) {
|
||||
if (!IsDestroying) {
|
||||
assert(!DestroyingOperatorDeletes.contains(FD->getCanonicalDecl()));
|
||||
return;
|
||||
}
|
||||
DestroyingOperatorDeletes.insert(FD->getCanonicalDecl());
|
||||
}
|
||||
|
||||
bool ASTContext::isDestroyingOperatorDelete(const FunctionDecl *FD) const {
|
||||
return DestroyingOperatorDeletes.contains(FD->getCanonicalDecl());
|
||||
}
|
||||
|
||||
void ASTContext::setIsTypeAwareOperatorNewOrDelete(const FunctionDecl *FD,
|
||||
bool IsTypeAware) {
|
||||
if (!IsTypeAware) {
|
||||
assert(!TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl()));
|
||||
return;
|
||||
}
|
||||
TypeAwareOperatorNewAndDeletes.insert(FD->getCanonicalDecl());
|
||||
}
|
||||
|
||||
bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
|
||||
return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
|
||||
}
|
||||
|
||||
MangleNumberingContext &
|
||||
ASTContext::getManglingNumberContext(const DeclContext *DC) {
|
||||
assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.
|
||||
|
@ -4042,6 +4042,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||
ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
|
||||
ToFunction->setFriendConstraintRefersToEnclosingTemplate(
|
||||
D->FriendConstraintRefersToEnclosingTemplate());
|
||||
ToFunction->setIsDestroyingOperatorDelete(D->isDestroyingOperatorDelete());
|
||||
ToFunction->setIsTypeAwareOperatorNewOrDelete(
|
||||
D->isTypeAwareOperatorNewOrDelete());
|
||||
ToFunction->setRangeEnd(ToEndLoc);
|
||||
ToFunction->setDefaultLoc(ToDefaultLoc);
|
||||
|
||||
@ -8338,10 +8341,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) {
|
||||
|
@ -3380,7 +3380,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
|
||||
// Always invalid.
|
||||
return this->emitInvalid(E);
|
||||
}
|
||||
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction())
|
||||
} else if (!OperatorNew
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation())
|
||||
return this->emitInvalidNewDeleteExpr(E, E);
|
||||
|
||||
const Descriptor *Desc;
|
||||
@ -3600,7 +3601,7 @@ bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
|
||||
|
||||
const FunctionDecl *OperatorDelete = E->getOperatorDelete();
|
||||
|
||||
if (!OperatorDelete->isReplaceableGlobalAllocationFunction())
|
||||
if (!OperatorDelete->isUsableAsGlobalAllocationFunctionInConstantEvaluation())
|
||||
return this->emitInvalidNewDeleteExpr(E, E);
|
||||
|
||||
// Arg must be an lvalue.
|
||||
@ -4820,9 +4821,9 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
|
||||
|
||||
const FunctionDecl *FuncDecl = E->getDirectCallee();
|
||||
// Calls to replaceable operator new/operator delete.
|
||||
if (FuncDecl && FuncDecl->isReplaceableGlobalAllocationFunction()) {
|
||||
if (FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_New ||
|
||||
FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_Array_New) {
|
||||
if (FuncDecl &&
|
||||
FuncDecl->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
if (FuncDecl->getDeclName().isAnyOperatorNew()) {
|
||||
return VisitBuiltinCallExpr(E, Builtin::BI__builtin_operator_new);
|
||||
} else {
|
||||
assert(FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_Delete);
|
||||
|
@ -1205,7 +1205,8 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
|
||||
if (const FunctionDecl *VirtualDelete =
|
||||
getVirtualOperatorDelete(AllocType);
|
||||
VirtualDelete &&
|
||||
!VirtualDelete->isReplaceableGlobalAllocationFunction()) {
|
||||
!VirtualDelete
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
S.FFDiag(S.Current->getSource(OpPC),
|
||||
diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
|
||||
@ -1723,7 +1724,9 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
|
||||
return true;
|
||||
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
|
||||
<< /*C++26 feature*/ 1 << E->getSourceRange();
|
||||
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
|
||||
} else if (
|
||||
!OperatorNew
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
S.FFDiag(S.Current->getSource(OpPC),
|
||||
diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
|
||||
@ -1741,7 +1744,8 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
|
||||
} else {
|
||||
const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
|
||||
const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
|
||||
if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
|
||||
if (!OperatorDelete
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
S.FFDiag(S.Current->getSource(OpPC),
|
||||
diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
|
||||
|
@ -3082,6 +3082,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
|
||||
static_cast<unsigned char>(DeductionCandidate::Normal);
|
||||
FunctionDeclBits.HasODRHash = false;
|
||||
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
|
||||
|
||||
if (TrailingRequiresClause)
|
||||
setTrailingRequiresClause(TrailingRequiresClause);
|
||||
}
|
||||
@ -3361,17 +3362,15 @@ bool FunctionDecl::isMSVCRTEntryPoint() const {
|
||||
}
|
||||
|
||||
bool FunctionDecl::isReservedGlobalPlacementOperator() 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)
|
||||
if (!getDeclName().isAnyOperatorNewOrDelete())
|
||||
return false;
|
||||
|
||||
if (!getDeclContext()->getRedeclContext()->isTranslationUnit())
|
||||
return false;
|
||||
|
||||
if (isTypeAwareOperatorNewOrDelete())
|
||||
return false;
|
||||
|
||||
const auto *proto = getType()->castAs<FunctionProtoType>();
|
||||
if (proto->getNumParams() != 2 || proto->isVariadic())
|
||||
return false;
|
||||
@ -3385,14 +3384,9 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
|
||||
return (proto->getParamType(1).getCanonicalType() == Context.VoidPtrTy);
|
||||
}
|
||||
|
||||
bool FunctionDecl::isReplaceableGlobalAllocationFunction(
|
||||
bool FunctionDecl::isUsableAsGlobalAllocationFunctionInConstantEvaluation(
|
||||
UnsignedOrNone *AlignmentParam, bool *IsNothrow) 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)
|
||||
if (!getDeclName().isAnyOperatorNewOrDelete())
|
||||
return false;
|
||||
|
||||
if (isa<CXXRecordDecl>(getDeclContext()))
|
||||
@ -3402,8 +3396,31 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(
|
||||
if (!getDeclContext()->getRedeclContext()->isTranslationUnit())
|
||||
return false;
|
||||
|
||||
if (isVariadic())
|
||||
return false;
|
||||
|
||||
if (isTypeAwareOperatorNewOrDelete()) {
|
||||
bool IsDelete = getDeclName().isAnyOperatorDelete();
|
||||
unsigned RequiredParameterCount =
|
||||
IsDelete ? FunctionDecl::RequiredTypeAwareDeleteParameterCount
|
||||
: FunctionDecl::RequiredTypeAwareNewParameterCount;
|
||||
if (AlignmentParam)
|
||||
*AlignmentParam =
|
||||
/* type identity */ 1U + /* address */ IsDelete + /* size */ 1U;
|
||||
if (RequiredParameterCount == getNumParams())
|
||||
return true;
|
||||
if (getNumParams() > RequiredParameterCount + 1)
|
||||
return false;
|
||||
if (!getParamDecl(RequiredParameterCount)->getType()->isNothrowT())
|
||||
return false;
|
||||
|
||||
if (IsNothrow)
|
||||
*IsNothrow = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto *FPT = getType()->castAs<FunctionProtoType>();
|
||||
if (FPT->getNumParams() == 0 || FPT->getNumParams() > 4 || FPT->isVariadic())
|
||||
if (FPT->getNumParams() == 0 || FPT->getNumParams() > 4)
|
||||
return false;
|
||||
|
||||
// If this is a single-parameter function, it must be a replaceable global
|
||||
@ -3423,8 +3440,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(
|
||||
// In C++14, the next parameter can be a 'std::size_t' for sized delete.
|
||||
bool IsSizedDelete = false;
|
||||
if (Ctx.getLangOpts().SizedDeallocation &&
|
||||
(getDeclName().getCXXOverloadedOperator() == OO_Delete ||
|
||||
getDeclName().getCXXOverloadedOperator() == OO_Array_Delete) &&
|
||||
getDeclName().isAnyOperatorDelete() &&
|
||||
Ctx.hasSameType(Ty, Ctx.getSizeType())) {
|
||||
IsSizedDelete = true;
|
||||
Consume();
|
||||
@ -3494,17 +3510,19 @@ bool FunctionDecl::isInlineBuiltinDeclaration() const {
|
||||
}
|
||||
|
||||
bool FunctionDecl::isDestroyingOperatorDelete() const {
|
||||
// C++ P0722:
|
||||
// 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)
|
||||
return false;
|
||||
return getASTContext().isDestroyingOperatorDelete(this);
|
||||
}
|
||||
|
||||
auto *RD = getParamDecl(1)->getType()->getAsCXXRecordDecl();
|
||||
return RD && RD->isInStdNamespace() && RD->getIdentifier() &&
|
||||
RD->getIdentifier()->isStr("destroying_delete_t");
|
||||
void FunctionDecl::setIsDestroyingOperatorDelete(bool IsDestroyingDelete) {
|
||||
getASTContext().setIsDestroyingOperatorDelete(this, IsDestroyingDelete);
|
||||
}
|
||||
|
||||
bool FunctionDecl::isTypeAwareOperatorNewOrDelete() const {
|
||||
return getASTContext().isTypeAwareOperatorNewOrDelete(this);
|
||||
}
|
||||
|
||||
void FunctionDecl::setIsTypeAwareOperatorNewOrDelete(bool IsTypeAware) {
|
||||
getASTContext().setIsTypeAwareOperatorNewOrDelete(this, IsTypeAware);
|
||||
}
|
||||
|
||||
LanguageLinkage FunctionDecl::getLanguageLinkage() const {
|
||||
|
@ -2530,13 +2530,46 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
|
||||
bool CXXMethodDecl::isUsualDeallocationFunction(
|
||||
SmallVectorImpl<const FunctionDecl *> &PreventedBy) const {
|
||||
assert(PreventedBy.empty() && "PreventedBy is expected to be empty");
|
||||
if (getOverloadedOperator() != OO_Delete &&
|
||||
getOverloadedOperator() != OO_Array_Delete)
|
||||
if (!getDeclName().isAnyOperatorDelete())
|
||||
return false;
|
||||
|
||||
if (isTypeAwareOperatorNewOrDelete()) {
|
||||
// A variadic type aware allocation function is not a usual deallocation
|
||||
// function
|
||||
if (isVariadic())
|
||||
return false;
|
||||
|
||||
// Type aware deallocation functions are only usual if they only accept the
|
||||
// mandatory arguments
|
||||
if (getNumParams() != FunctionDecl::RequiredTypeAwareDeleteParameterCount)
|
||||
return false;
|
||||
|
||||
FunctionTemplateDecl *PrimaryTemplate = getPrimaryTemplate();
|
||||
if (!PrimaryTemplate)
|
||||
return true;
|
||||
|
||||
// A template instance is is only a usual deallocation function if it has a
|
||||
// type-identity parameter, the type-identity parameter is a dependent type
|
||||
// (i.e. the type-identity parameter is of type std::type_identity<U> where
|
||||
// U shall be a dependent type), and the type-identity parameter is the only
|
||||
// dependent parameter, and there are no template packs in the parameter
|
||||
// list.
|
||||
FunctionDecl *SpecializedDecl = PrimaryTemplate->getTemplatedDecl();
|
||||
if (!SpecializedDecl->getParamDecl(0)->getType()->isDependentType())
|
||||
return false;
|
||||
for (unsigned Idx = 1; Idx < getNumParams(); ++Idx) {
|
||||
if (SpecializedDecl->getParamDecl(Idx)->getType()->isDependentType())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// C++ [basic.stc.dynamic.deallocation]p2:
|
||||
// A template instance is never a usual deallocation function,
|
||||
// regardless of its signature.
|
||||
// Post-P2719 adoption:
|
||||
// A template instance is is only a usual deallocation function if it has a
|
||||
// type-identity parameter
|
||||
if (getPrimaryTemplate())
|
||||
return false;
|
||||
|
||||
|
@ -226,7 +226,8 @@ SourceLocation CXXScalarValueInitExpr::getBeginLoc() const {
|
||||
|
||||
// CXXNewExpr
|
||||
CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
|
||||
FunctionDecl *OperatorDelete, bool ShouldPassAlignment,
|
||||
FunctionDecl *OperatorDelete,
|
||||
const ImplicitAllocationParameters &IAP,
|
||||
bool UsualArrayDeleteWantsSize,
|
||||
ArrayRef<Expr *> PlacementArgs, SourceRange TypeIdParens,
|
||||
std::optional<Expr *> ArraySize,
|
||||
@ -245,7 +246,9 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
|
||||
|
||||
CXXNewExprBits.IsGlobalNew = IsGlobalNew;
|
||||
CXXNewExprBits.IsArray = ArraySize.has_value();
|
||||
CXXNewExprBits.ShouldPassAlignment = ShouldPassAlignment;
|
||||
CXXNewExprBits.ShouldPassAlignment = isAlignedAllocation(IAP.PassAlignment);
|
||||
CXXNewExprBits.ShouldPassTypeIdentity =
|
||||
isTypeAwareAllocation(IAP.PassTypeIdentity);
|
||||
CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize;
|
||||
CXXNewExprBits.HasInitializer = Initializer != nullptr;
|
||||
CXXNewExprBits.StoredInitializationStyle =
|
||||
@ -290,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, const ImplicitAllocationParameters &IAP,
|
||||
bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs,
|
||||
SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
|
||||
CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
|
||||
@ -304,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,
|
||||
|
@ -8385,9 +8385,8 @@ public:
|
||||
FD = CorrespondingCallOpSpecialization;
|
||||
} else
|
||||
FD = LambdaCallOp;
|
||||
} else if (FD->isReplaceableGlobalAllocationFunction()) {
|
||||
if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
|
||||
FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) {
|
||||
} else if (FD->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
if (FD->getDeclName().isAnyOperatorNew()) {
|
||||
LValue Ptr;
|
||||
if (!HandleOperatorNewCall(Info, E, Ptr))
|
||||
return false;
|
||||
@ -10319,7 +10318,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
|
||||
Info.FFDiag(E, diag::note_constexpr_new_placement)
|
||||
<< /*Unsupported*/ 0 << E->getSourceRange();
|
||||
return false;
|
||||
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
|
||||
} else if (!OperatorNew
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
|
||||
return false;
|
||||
@ -16524,7 +16524,8 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
|
||||
return false;
|
||||
|
||||
FunctionDecl *OperatorDelete = E->getOperatorDelete();
|
||||
if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
|
||||
if (!OperatorDelete
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
|
||||
return false;
|
||||
@ -16568,7 +16569,8 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
|
||||
if (!E->isArrayForm() && !E->isGlobalDelete()) {
|
||||
const FunctionDecl *VirtualDelete = getVirtualOperatorDelete(AllocType);
|
||||
if (VirtualDelete &&
|
||||
!VirtualDelete->isReplaceableGlobalAllocationFunction()) {
|
||||
!VirtualDelete
|
||||
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
|
||||
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
|
||||
return false;
|
||||
|
@ -1382,9 +1382,10 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
|
||||
namespace {
|
||||
/// The parameters to pass to a usual operator delete.
|
||||
struct UsualDeleteParams {
|
||||
TypeAwareAllocationMode TypeAwareDelete = TypeAwareAllocationMode::No;
|
||||
bool DestroyingDelete = false;
|
||||
bool Size = false;
|
||||
bool Alignment = false;
|
||||
AlignedAllocationMode Alignment = AlignedAllocationMode::No;
|
||||
};
|
||||
}
|
||||
|
||||
@ -1394,11 +1395,20 @@ 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()) {
|
||||
Params.TypeAwareDelete = TypeAwareAllocationMode::Yes;
|
||||
assert(AI != AE);
|
||||
++AI;
|
||||
}
|
||||
|
||||
// The first argument after the type-identity parameter (if any) is
|
||||
// always a void* (or C* for a destroying operator delete for class
|
||||
// type C).
|
||||
++AI;
|
||||
|
||||
// The next parameter may be a std::destroying_delete_t.
|
||||
if (FD->isDestroyingOperatorDelete()) {
|
||||
assert(!isTypeAwareAllocation(Params.TypeAwareDelete));
|
||||
Params.DestroyingDelete = true;
|
||||
assert(AI != AE);
|
||||
++AI;
|
||||
@ -1408,12 +1418,14 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
|
||||
if (AI != AE && (*AI)->isIntegerType()) {
|
||||
Params.Size = true;
|
||||
++AI;
|
||||
}
|
||||
} else
|
||||
assert(!isTypeAwareAllocation(Params.TypeAwareDelete));
|
||||
|
||||
if (AI != AE && (*AI)->isAlignValT()) {
|
||||
Params.Alignment = true;
|
||||
Params.Alignment = AlignedAllocationMode::Yes;
|
||||
++AI;
|
||||
}
|
||||
} else
|
||||
assert(!isTypeAwareAllocation(Params.TypeAwareDelete));
|
||||
|
||||
assert(AI == AE && "unexpected usual deallocation function parameter");
|
||||
return Params;
|
||||
@ -1434,10 +1446,13 @@ namespace {
|
||||
QualType ArgType;
|
||||
};
|
||||
|
||||
unsigned NumPlacementArgs : 31;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned NumPlacementArgs : 30;
|
||||
LLVM_PREFERRED_TYPE(AlignedAllocationMode)
|
||||
unsigned PassAlignmentToPlacementDelete : 1;
|
||||
LLVM_PREFERRED_TYPE(TypeAwareAllocationMode)
|
||||
unsigned PassTypeToPlacementDelete : 1;
|
||||
const FunctionDecl *OperatorDelete;
|
||||
RValueTy TypeIdentity;
|
||||
ValueTy Ptr;
|
||||
ValueTy AllocSize;
|
||||
CharUnits AllocAlign;
|
||||
@ -1452,13 +1467,15 @@ namespace {
|
||||
}
|
||||
|
||||
CallDeleteDuringNew(size_t NumPlacementArgs,
|
||||
const FunctionDecl *OperatorDelete, ValueTy Ptr,
|
||||
ValueTy AllocSize, bool PassAlignmentToPlacementDelete,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
RValueTy TypeIdentity, ValueTy Ptr, ValueTy AllocSize,
|
||||
const ImplicitAllocationParameters &IAP,
|
||||
CharUnits AllocAlign)
|
||||
: NumPlacementArgs(NumPlacementArgs),
|
||||
PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete),
|
||||
OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
|
||||
AllocAlign(AllocAlign) {}
|
||||
: NumPlacementArgs(NumPlacementArgs),
|
||||
PassAlignmentToPlacementDelete(
|
||||
isAlignedAllocation(IAP.PassAlignment)),
|
||||
OperatorDelete(OperatorDelete), TypeIdentity(TypeIdentity), Ptr(Ptr),
|
||||
AllocSize(AllocSize), AllocAlign(AllocAlign) {}
|
||||
|
||||
void setPlacementArg(unsigned I, RValueTy Arg, QualType Type) {
|
||||
assert(I < NumPlacementArgs && "index out of range");
|
||||
@ -1468,17 +1485,28 @@ namespace {
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
const auto *FPT = OperatorDelete->getType()->castAs<FunctionProtoType>();
|
||||
CallArgList DeleteArgs;
|
||||
|
||||
// The first argument is always a void* (or C* for a destroying operator
|
||||
// delete for class type C).
|
||||
DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0));
|
||||
unsigned FirstNonTypeArg = 0;
|
||||
TypeAwareAllocationMode TypeAwareDeallocation =
|
||||
TypeAwareAllocationMode::No;
|
||||
if (OperatorDelete->isTypeAwareOperatorNewOrDelete()) {
|
||||
TypeAwareDeallocation = TypeAwareAllocationMode::Yes;
|
||||
QualType SpecializedTypeIdentity = FPT->getParamType(0);
|
||||
++FirstNonTypeArg;
|
||||
DeleteArgs.add(Traits::get(CGF, TypeIdentity), SpecializedTypeIdentity);
|
||||
}
|
||||
// The first argument after type-identity parameter (if any) is always
|
||||
// a void* (or C* for a destroying operator delete for class type C).
|
||||
DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(FirstNonTypeArg));
|
||||
|
||||
// Figure out what other parameters we should be implicitly passing.
|
||||
UsualDeleteParams Params;
|
||||
if (NumPlacementArgs) {
|
||||
// A placement deallocation function is implicitly passed an alignment
|
||||
// if the placement allocation function was, but is never passed a size.
|
||||
Params.Alignment = PassAlignmentToPlacementDelete;
|
||||
Params.Alignment =
|
||||
alignedAllocationModeFromBool(PassAlignmentToPlacementDelete);
|
||||
Params.TypeAwareDelete = TypeAwareDeallocation;
|
||||
Params.Size = isTypeAwareAllocation(Params.TypeAwareDelete);
|
||||
} else {
|
||||
// For a non-placement new-expression, 'operator delete' can take a
|
||||
// size and/or an alignment if it has the right parameters.
|
||||
@ -1497,7 +1525,7 @@ namespace {
|
||||
// is an enum whose underlying type is std::size_t.
|
||||
// FIXME: Use the right type as the parameter type. Note that in a call
|
||||
// to operator delete(size_t, ...), we may not have it available.
|
||||
if (Params.Alignment)
|
||||
if (isAlignedAllocation(Params.Alignment))
|
||||
DeleteArgs.add(RValue::get(llvm::ConstantInt::get(
|
||||
CGF.SizeTy, AllocAlign.getQuantity())),
|
||||
CGF.getContext().getSizeType());
|
||||
@ -1516,13 +1544,11 @@ namespace {
|
||||
|
||||
/// Enter a cleanup to call 'operator delete' if the initializer in a
|
||||
/// new-expression throws.
|
||||
static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
|
||||
const CXXNewExpr *E,
|
||||
Address NewPtr,
|
||||
llvm::Value *AllocSize,
|
||||
CharUnits AllocAlign,
|
||||
static void EnterNewDeleteCleanup(CodeGenFunction &CGF, const CXXNewExpr *E,
|
||||
RValue TypeIdentity, Address NewPtr,
|
||||
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.
|
||||
@ -1538,7 +1564,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
|
||||
|
||||
DirectCleanup *Cleanup = CGF.EHStack.pushCleanupWithExtra<DirectCleanup>(
|
||||
EHCleanup, E->getNumPlacementArgs(), E->getOperatorDelete(),
|
||||
NewPtr.emitRawPointer(CGF), AllocSize, E->passAlignment(), AllocAlign);
|
||||
TypeIdentity, NewPtr.emitRawPointer(CGF), AllocSize,
|
||||
E->implicitAllocationParameters(), AllocAlign);
|
||||
for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
|
||||
auto &Arg = NewArgs[I + NumNonPlacementArgs];
|
||||
Cleanup->setPlacementArg(I, Arg.getRValue(CGF), Arg.Ty);
|
||||
@ -1552,7 +1579,8 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
|
||||
DominatingValue<RValue>::save(CGF, RValue::get(NewPtr, CGF));
|
||||
DominatingValue<RValue>::saved_type SavedAllocSize =
|
||||
DominatingValue<RValue>::save(CGF, RValue::get(AllocSize));
|
||||
|
||||
DominatingValue<RValue>::saved_type SavedTypeIdentity =
|
||||
DominatingValue<RValue>::save(CGF, TypeIdentity);
|
||||
struct ConditionalCleanupTraits {
|
||||
typedef DominatingValue<RValue>::saved_type ValueTy;
|
||||
typedef DominatingValue<RValue>::saved_type RValueTy;
|
||||
@ -1562,14 +1590,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(),
|
||||
SavedTypeIdentity, SavedNewPtr, SavedAllocSize,
|
||||
E->implicitAllocationParameters(), AllocAlign);
|
||||
for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
|
||||
auto &Arg = NewArgs[I + NumNonPlacementArgs];
|
||||
Cleanup->setPlacementArg(
|
||||
@ -1589,6 +1614,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);
|
||||
@ -1615,6 +1641,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
||||
// operator, just "inline" it directly.
|
||||
Address allocation = Address::invalid();
|
||||
CallArgList allocatorArgs;
|
||||
RValue TypeIdentityArg;
|
||||
if (allocator->isReservedGlobalPlacementOperator()) {
|
||||
assert(E->getNumPlacementArgs() == 1);
|
||||
const Expr *arg = *E->placement_arguments().begin();
|
||||
@ -1639,8 +1666,17 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
||||
} else {
|
||||
const FunctionProtoType *allocatorType =
|
||||
allocator->getType()->castAs<FunctionProtoType>();
|
||||
ImplicitAllocationParameters IAP = E->implicitAllocationParameters();
|
||||
unsigned ParamsToSkip = 0;
|
||||
|
||||
if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
|
||||
QualType SpecializedTypeIdentity = allocatorType->getParamType(0);
|
||||
CXXScalarValueInitExpr TypeIdentityParam(SpecializedTypeIdentity, nullptr,
|
||||
SourceLocation());
|
||||
TypeIdentityArg = EmitAnyExprToTemp(&TypeIdentityParam);
|
||||
allocatorArgs.add(TypeIdentityArg, SpecializedTypeIdentity);
|
||||
++ParamsToSkip;
|
||||
++IndexOfAlignArg;
|
||||
}
|
||||
// The allocation size is the first argument.
|
||||
QualType sizeType = getContext().getSizeType();
|
||||
allocatorArgs.add(RValue::get(allocSize), sizeType);
|
||||
@ -1652,10 +1688,10 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
||||
}
|
||||
|
||||
// The allocation alignment may be passed as the second argument.
|
||||
if (E->passAlignment()) {
|
||||
if (isAlignedAllocation(IAP.PassAlignment)) {
|
||||
QualType AlignValT = sizeType;
|
||||
if (allocatorType->getNumParams() > 1) {
|
||||
AlignValT = allocatorType->getParamType(1);
|
||||
if (allocatorType->getNumParams() > IndexOfAlignArg) {
|
||||
AlignValT = allocatorType->getParamType(IndexOfAlignArg);
|
||||
assert(getContext().hasSameUnqualifiedType(
|
||||
AlignValT->castAs<EnumType>()->getDecl()->getIntegerType(),
|
||||
sizeType) &&
|
||||
@ -1732,8 +1768,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
||||
llvm::Instruction *cleanupDominator = nullptr;
|
||||
if (E->getOperatorDelete() &&
|
||||
!E->getOperatorDelete()->isReservedGlobalPlacementOperator()) {
|
||||
EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocAlign,
|
||||
allocatorArgs);
|
||||
EnterNewDeleteCleanup(*this, E, TypeIdentityArg, allocation, allocSize,
|
||||
allocAlign, allocatorArgs);
|
||||
operatorDeleteCleanup = EHStack.stable_begin();
|
||||
cleanupDominator = Builder.CreateUnreachable();
|
||||
}
|
||||
@ -1811,21 +1847,29 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
|
||||
auto Params = getUsualDeleteParams(DeleteFD);
|
||||
auto ParamTypeIt = DeleteFTy->param_type_begin();
|
||||
|
||||
std::optional<llvm::AllocaInst *> TagAlloca;
|
||||
auto EmitTag = [&](QualType TagType, const char *TagName) {
|
||||
assert(!TagAlloca);
|
||||
llvm::Type *Ty = getTypes().ConvertType(TagType);
|
||||
CharUnits Align = CGM.getNaturalTypeAlignment(TagType);
|
||||
llvm::AllocaInst *TagAllocation = CreateTempAlloca(Ty, TagName);
|
||||
TagAllocation->setAlignment(Align.getAsAlign());
|
||||
DeleteArgs.add(RValue::getAggregate(Address(TagAllocation, Ty, Align)),
|
||||
TagType);
|
||||
TagAlloca = TagAllocation;
|
||||
};
|
||||
|
||||
// Pass std::type_identity tag if present
|
||||
if (isTypeAwareAllocation(Params.TypeAwareDelete))
|
||||
EmitTag(*ParamTypeIt++, "typeaware.delete.tag");
|
||||
|
||||
// Pass the pointer itself.
|
||||
QualType ArgTy = *ParamTypeIt++;
|
||||
DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
|
||||
|
||||
// Pass the std::destroying_delete tag if present.
|
||||
llvm::AllocaInst *DestroyingDeleteTag = nullptr;
|
||||
if (Params.DestroyingDelete) {
|
||||
QualType DDTag = *ParamTypeIt++;
|
||||
llvm::Type *Ty = getTypes().ConvertType(DDTag);
|
||||
CharUnits Align = CGM.getNaturalTypeAlignment(DDTag);
|
||||
DestroyingDeleteTag = CreateTempAlloca(Ty, "destroying.delete.tag");
|
||||
DestroyingDeleteTag->setAlignment(Align.getAsAlign());
|
||||
DeleteArgs.add(
|
||||
RValue::getAggregate(Address(DestroyingDeleteTag, Ty, Align)), DDTag);
|
||||
}
|
||||
if (Params.DestroyingDelete)
|
||||
EmitTag(*ParamTypeIt++, "destroying.delete.tag");
|
||||
|
||||
// Pass the size if the delete function has a size_t parameter.
|
||||
if (Params.Size) {
|
||||
@ -1847,7 +1891,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
|
||||
}
|
||||
|
||||
// Pass the alignment if the delete function has an align_val_t parameter.
|
||||
if (Params.Alignment) {
|
||||
if (isAlignedAllocation(Params.Alignment)) {
|
||||
QualType AlignValType = *ParamTypeIt++;
|
||||
CharUnits DeleteTypeAlign =
|
||||
getContext().toCharUnitsFromBits(getContext().getTypeAlignIfKnown(
|
||||
@ -1863,12 +1907,11 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
|
||||
// Emit the call to delete.
|
||||
EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs);
|
||||
|
||||
// If call argument lowering didn't use the destroying_delete_t alloca,
|
||||
// remove it again.
|
||||
if (DestroyingDeleteTag && DestroyingDeleteTag->use_empty())
|
||||
DestroyingDeleteTag->eraseFromParent();
|
||||
// If call argument lowering didn't use a generated tag argument alloca we
|
||||
// remove them
|
||||
if (TagAlloca && (*TagAlloca)->use_empty())
|
||||
(*TagAlloca)->eraseFromParent();
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Calls the given 'operator delete' on a single object.
|
||||
struct CallObjectDelete final : EHScopeStack::Cleanup {
|
||||
|
@ -776,6 +776,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
|
||||
if (LangOpts.Char8)
|
||||
Builder.defineMacro("__cpp_char8_t", "202207L");
|
||||
Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
|
||||
|
||||
// TODO: Final number?
|
||||
Builder.defineMacro("__cpp_type_aware_allocators", "202500L");
|
||||
}
|
||||
|
||||
/// InitializeOpenCLFeatureTestMacros - Define OpenCL macros based on target
|
||||
|
@ -258,6 +258,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
|
||||
VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr),
|
||||
StdCoroutineTraitsCache(nullptr), IdResolver(pp),
|
||||
OriginalLexicalContext(nullptr), StdInitializerList(nullptr),
|
||||
StdTypeIdentity(nullptr),
|
||||
FullyCheckedComparisonCategories(
|
||||
static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
|
||||
StdSourceLocationImplDecl(nullptr), CXXTypeInfoDecl(nullptr),
|
||||
|
@ -1097,12 +1097,39 @@ static TypeSourceInfo *getTypeSourceInfoForStdAlignValT(Sema &S,
|
||||
return S.Context.getTrivialTypeSourceInfo(StdAlignValDecl);
|
||||
}
|
||||
|
||||
// When searching for custom allocators on the PromiseType we want to
|
||||
// warn that we will ignore type aware allocators.
|
||||
static bool DiagnoseTypeAwareAllocators(Sema &S, SourceLocation Loc,
|
||||
unsigned DiagnosticID,
|
||||
DeclarationName Name,
|
||||
QualType PromiseType) {
|
||||
assert(PromiseType->isRecordType());
|
||||
|
||||
LookupResult R(S, Name, Loc, Sema::LookupOrdinaryName);
|
||||
S.LookupQualifiedName(R, PromiseType->getAsCXXRecordDecl());
|
||||
bool HaveIssuedWarning = false;
|
||||
for (auto Decl : R) {
|
||||
if (!Decl->getAsFunction()->isTypeAwareOperatorNewOrDelete())
|
||||
continue;
|
||||
if (!HaveIssuedWarning) {
|
||||
S.Diag(Loc, DiagnosticID) << Name;
|
||||
HaveIssuedWarning = true;
|
||||
}
|
||||
S.Diag(Decl->getLocation(), diag::note_type_aware_operator_declared)
|
||||
<< /* isTypeAware=*/1 << Decl << Decl->getDeclContext();
|
||||
}
|
||||
R.suppressDiagnostics();
|
||||
return HaveIssuedWarning;
|
||||
}
|
||||
|
||||
// Find an appropriate delete for the promise.
|
||||
static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseType,
|
||||
FunctionDecl *&OperatorDelete) {
|
||||
DeclarationName DeleteName =
|
||||
S.Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
|
||||
DiagnoseTypeAwareAllocators(S, Loc,
|
||||
diag::warn_coroutine_type_aware_allocator_ignored,
|
||||
DeleteName, PromiseType);
|
||||
auto *PointeeRD = PromiseType->getAsCXXRecordDecl();
|
||||
assert(PointeeRD && "PromiseType must be a CxxRecordDecl type");
|
||||
|
||||
@ -1112,9 +1139,10 @@ 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 = {
|
||||
alignedAllocationModeFromBool(Overaligned), SizedDeallocationMode::Yes};
|
||||
if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
|
||||
/*Diagnose*/ true, /*WantSize*/ true,
|
||||
/*WantAligned*/ Overaligned))
|
||||
IDP, /*Diagnose=*/true))
|
||||
return false;
|
||||
|
||||
// [dcl.fct.def.coroutine]p12
|
||||
@ -1125,18 +1153,18 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
|
||||
// shall be the function with one parameter.
|
||||
if (!OperatorDelete) {
|
||||
// Look for a global declaration.
|
||||
// Coroutines can always provide their required size.
|
||||
const bool CanProvideSize = true;
|
||||
// Sema::FindUsualDeallocationFunction will try to find the one with two
|
||||
// parameters first. It will return the deallocation function with one
|
||||
// parameter if failed.
|
||||
OperatorDelete = S.FindUsualDeallocationFunction(Loc, CanProvideSize,
|
||||
Overaligned, DeleteName);
|
||||
// Coroutines can always provide their required size.
|
||||
IDP.PassSize = SizedDeallocationMode::Yes;
|
||||
OperatorDelete = S.FindUsualDeallocationFunction(Loc, IDP, DeleteName);
|
||||
|
||||
if (!OperatorDelete)
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!OperatorDelete->isTypeAwareOperatorNewOrDelete());
|
||||
S.MarkFunctionReferenced(Loc, OperatorDelete);
|
||||
return true;
|
||||
}
|
||||
@ -1411,10 +1439,10 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
|
||||
FunctionDecl *OperatorNew = nullptr;
|
||||
SmallVector<Expr *, 1> PlacementArgs;
|
||||
DeclarationName NewName =
|
||||
S.getASTContext().DeclarationNames.getCXXOperatorName(OO_New);
|
||||
|
||||
const bool PromiseContainsNew = [this, &PromiseType]() -> bool {
|
||||
DeclarationName NewName =
|
||||
S.getASTContext().DeclarationNames.getCXXOperatorName(OO_New);
|
||||
const bool PromiseContainsNew = [this, &PromiseType, NewName]() -> bool {
|
||||
LookupResult R(S, NewName, Loc, Sema::LookupOrdinaryName);
|
||||
|
||||
if (PromiseType->isRecordType())
|
||||
@ -1425,7 +1453,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
|
||||
// Helper function to indicate whether the last lookup found the aligned
|
||||
// allocation function.
|
||||
bool PassAlignment = S.getLangOpts().CoroAlignedAllocation;
|
||||
ImplicitAllocationParameters IAP(
|
||||
alignedAllocationModeFromBool(S.getLangOpts().CoroAlignedAllocation));
|
||||
auto LookupAllocationFunction = [&](Sema::AllocationFunctionScope NewScope =
|
||||
Sema::AFS_Both,
|
||||
bool WithoutPlacementArgs = false,
|
||||
@ -1439,14 +1468,19 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
if (NewScope == Sema::AFS_Both)
|
||||
NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
|
||||
|
||||
PassAlignment = !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
|
||||
bool ShouldUseAlignedAlloc =
|
||||
!ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
|
||||
IAP = ImplicitAllocationParameters(
|
||||
alignedAllocationModeFromBool(ShouldUseAlignedAlloc));
|
||||
|
||||
FunctionDecl *UnusedResult = nullptr;
|
||||
S.FindAllocationFunctions(Loc, SourceRange(), NewScope,
|
||||
/*DeleteScope*/ Sema::AFS_Both, PromiseType,
|
||||
/*isArray*/ false, PassAlignment,
|
||||
/*DeleteScope=*/Sema::AFS_Both, PromiseType,
|
||||
/*isArray=*/false, IAP,
|
||||
WithoutPlacementArgs ? MultiExprArg{}
|
||||
: PlacementArgs,
|
||||
OperatorNew, UnusedResult, /*Diagnose*/ false);
|
||||
OperatorNew, UnusedResult, /*Diagnose=*/false);
|
||||
assert(!OperatorNew || !OperatorNew->isTypeAwareOperatorNewOrDelete());
|
||||
};
|
||||
|
||||
// We don't expect to call to global operator new with (size, p0, …, pn).
|
||||
@ -1470,8 +1504,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
// by passing the amount of space requested as an argument of type
|
||||
// std::size_t as the first argument, and the requested alignment as
|
||||
// an argument of type std:align_val_t as the second argument.
|
||||
if (!OperatorNew ||
|
||||
(S.getLangOpts().CoroAlignedAllocation && !PassAlignment))
|
||||
if (!OperatorNew || (S.getLangOpts().CoroAlignedAllocation &&
|
||||
!isAlignedAllocation(IAP.PassAlignment)))
|
||||
LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
|
||||
/*WithoutPlacementArgs*/ true);
|
||||
}
|
||||
@ -1496,7 +1530,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
// Helper variable to emit warnings.
|
||||
bool FoundNonAlignedInPromise = false;
|
||||
if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
|
||||
if (!OperatorNew || !PassAlignment) {
|
||||
if (!OperatorNew || !isAlignedAllocation(IAP.PassAlignment)) {
|
||||
FoundNonAlignedInPromise = OperatorNew;
|
||||
|
||||
LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
|
||||
@ -1533,14 +1567,22 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
}
|
||||
|
||||
if (!OperatorNew) {
|
||||
if (PromiseContainsNew)
|
||||
if (PromiseContainsNew) {
|
||||
S.Diag(Loc, diag::err_coroutine_unusable_new) << PromiseType << &FD;
|
||||
else if (RequiresNoThrowAlloc)
|
||||
DiagnoseTypeAwareAllocators(
|
||||
S, Loc, diag::note_coroutine_unusable_type_aware_allocators, NewName,
|
||||
PromiseType);
|
||||
} else if (RequiresNoThrowAlloc)
|
||||
S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new)
|
||||
<< &FD << S.getLangOpts().CoroAlignedAllocation;
|
||||
|
||||
return false;
|
||||
}
|
||||
assert(!OperatorNew->isTypeAwareOperatorNewOrDelete());
|
||||
|
||||
DiagnoseTypeAwareAllocators(S, Loc,
|
||||
diag::warn_coroutine_type_aware_allocator_ignored,
|
||||
NewName, PromiseType);
|
||||
|
||||
if (RequiresNoThrowAlloc) {
|
||||
const auto *FT = OperatorNew->getType()->castAs<FunctionProtoType>();
|
||||
@ -1562,6 +1604,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!OperatorDelete->isTypeAwareOperatorNewOrDelete());
|
||||
|
||||
Expr *FramePtr =
|
||||
S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_frame, {});
|
||||
|
||||
@ -1591,7 +1635,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
||||
return false;
|
||||
|
||||
SmallVector<Expr *, 2> NewArgs(1, FrameSize);
|
||||
if (S.getLangOpts().CoroAlignedAllocation && PassAlignment)
|
||||
if (S.getLangOpts().CoroAlignedAllocation &&
|
||||
isAlignedAllocation(IAP.PassAlignment))
|
||||
NewArgs.push_back(FrameAlignment);
|
||||
|
||||
if (OperatorNew->getNumParams() > NewArgs.size())
|
||||
|
@ -10169,10 +10169,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
// deallocation function shall not be declared with the consteval
|
||||
// specifier.
|
||||
if (ConstexprKind == ConstexprSpecKind::Consteval &&
|
||||
(NewFD->getOverloadedOperator() == OO_New ||
|
||||
NewFD->getOverloadedOperator() == OO_Array_New ||
|
||||
NewFD->getOverloadedOperator() == OO_Delete ||
|
||||
NewFD->getOverloadedOperator() == OO_Array_Delete)) {
|
||||
NewFD->getDeclName().isAnyOperatorNewOrDelete()) {
|
||||
Diag(D.getDeclSpec().getConstexprSpecLoc(),
|
||||
diag::err_invalid_consteval_decl_kind)
|
||||
<< NewFD;
|
||||
@ -10276,9 +10273,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
// A deallocation function with no exception-specification is treated
|
||||
// as if it were specified with noexcept(true).
|
||||
const FunctionProtoType *FPT = R->getAs<FunctionProtoType>();
|
||||
if ((Name.getCXXOverloadedOperator() == OO_Delete ||
|
||||
Name.getCXXOverloadedOperator() == OO_Array_Delete) &&
|
||||
getLangOpts().CPlusPlus11 && FPT && !FPT->hasExceptionSpec())
|
||||
if (Name.isAnyOperatorDelete() && getLangOpts().CPlusPlus11 && FPT &&
|
||||
!FPT->hasExceptionSpec())
|
||||
NewFD->setType(Context.getFunctionType(
|
||||
FPT->getReturnType(), FPT->getParamTypes(),
|
||||
FPT->getExtProtoInfo().withExceptionSpec(EST_BasicNoexcept)));
|
||||
|
@ -46,6 +46,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"
|
||||
@ -7322,6 +7323,48 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
|
||||
else if (Record->hasAttr<CUDADeviceBuiltinTextureTypeAttr>())
|
||||
checkCUDADeviceBuiltinTextureClassTemplate(*this, Record);
|
||||
}
|
||||
|
||||
llvm::SmallDenseMap<OverloadedOperatorKind,
|
||||
llvm::SmallVector<const FunctionDecl *, 2>, 4>
|
||||
TypeAwareDecls{{OO_New, {}},
|
||||
{OO_Array_New, {}},
|
||||
{OO_Delete, {}},
|
||||
{OO_Array_New, {}}};
|
||||
for (auto *D : Record->decls()) {
|
||||
const FunctionDecl *FnDecl = D->getAsFunction();
|
||||
if (!FnDecl || !FnDecl->isTypeAwareOperatorNewOrDelete())
|
||||
continue;
|
||||
assert(FnDecl->getDeclName().isAnyOperatorNewOrDelete());
|
||||
TypeAwareDecls[FnDecl->getOverloadedOperator()].push_back(FnDecl);
|
||||
}
|
||||
auto CheckMismatchedTypeAwareAllocators =
|
||||
[this, &TypeAwareDecls, Record](OverloadedOperatorKind NewKind,
|
||||
OverloadedOperatorKind DeleteKind) {
|
||||
auto &NewDecls = TypeAwareDecls[NewKind];
|
||||
auto &DeleteDecls = TypeAwareDecls[DeleteKind];
|
||||
if (NewDecls.empty() == DeleteDecls.empty())
|
||||
return;
|
||||
DeclarationName FoundOperator =
|
||||
Context.DeclarationNames.getCXXOperatorName(
|
||||
NewDecls.empty() ? DeleteKind : NewKind);
|
||||
DeclarationName MissingOperator =
|
||||
Context.DeclarationNames.getCXXOperatorName(
|
||||
NewDecls.empty() ? NewKind : DeleteKind);
|
||||
Diag(Record->getLocation(),
|
||||
diag::err_type_aware_allocator_missing_matching_operator)
|
||||
<< FoundOperator << Context.getRecordType(Record)
|
||||
<< MissingOperator;
|
||||
for (auto MD : NewDecls)
|
||||
Diag(MD->getLocation(),
|
||||
diag::note_unmatched_type_aware_allocator_declared)
|
||||
<< MD;
|
||||
for (auto MD : DeleteDecls)
|
||||
Diag(MD->getLocation(),
|
||||
diag::note_unmatched_type_aware_allocator_declared)
|
||||
<< MD;
|
||||
};
|
||||
CheckMismatchedTypeAwareAllocators(OO_New, OO_Delete);
|
||||
CheckMismatchedTypeAwareAllocators(OO_Array_New, OO_Array_Delete);
|
||||
}
|
||||
|
||||
/// Look up the special member function that would be called by a special
|
||||
@ -9849,10 +9892,15 @@ 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;
|
||||
QualType DeallocType = Context.getRecordType(RD);
|
||||
DeclarationName Name =
|
||||
Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
ImplicitDeallocationParameters IDP = {
|
||||
DeallocType, ShouldUseTypeAwareOperatorNewOrDelete(),
|
||||
AlignedAllocationMode::No, SizedDeallocationMode::No};
|
||||
if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
|
||||
OperatorDelete, /*Diagnose*/false)) {
|
||||
OperatorDelete, IDP,
|
||||
/*Diagnose=*/false)) {
|
||||
if (Diagnose)
|
||||
Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete);
|
||||
return true;
|
||||
@ -11021,14 +11069,18 @@ 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 AddressParamIndex = 0;
|
||||
if (OperatorDelete->isTypeAwareOperatorNewOrDelete())
|
||||
++AddressParamIndex;
|
||||
QualType ParamType =
|
||||
OperatorDelete->getParamDecl(AddressParamIndex)->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(AddressParamIndex)->getLocation());
|
||||
assert(!This.isInvalid() && "couldn't form 'this' expr in dtor?");
|
||||
This = PerformImplicitConversion(This.get(), ParamType,
|
||||
AssignmentAction::Passing);
|
||||
@ -11902,6 +11954,7 @@ NamespaceDecl *Sema::getStdNamespace() const {
|
||||
return cast_or_null<NamespaceDecl>(
|
||||
StdNamespace.get(Context.getExternalSource()));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
enum UnsupportedSTLSelect {
|
||||
@ -12058,25 +12111,40 @@ NamespaceDecl *Sema::getOrCreateStdNamespace() {
|
||||
return getStdNamespace();
|
||||
}
|
||||
|
||||
bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"Looking for std::initializer_list outside of C++.");
|
||||
|
||||
static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
|
||||
const char *ClassName,
|
||||
ClassTemplateDecl **CachedDecl,
|
||||
const Decl **MalformedDecl) {
|
||||
// We're looking for implicit instantiations of
|
||||
// template <typename E> class std::initializer_list.
|
||||
// template <typename U> class std::{ClassName}.
|
||||
|
||||
if (!StdNamespace) // If we haven't seen namespace std yet, this can't be it.
|
||||
if (!S.StdNamespace) // If we haven't seen namespace std yet, this can't be
|
||||
// it.
|
||||
return false;
|
||||
|
||||
auto ReportMatchingNameAsMalformed = [&](NamedDecl *D) {
|
||||
if (!MalformedDecl)
|
||||
return;
|
||||
if (!D)
|
||||
D = SugaredType->getAsTagDecl();
|
||||
if (!D || !D->isInStdNamespace())
|
||||
return;
|
||||
IdentifierInfo *II = D->getDeclName().getAsIdentifierInfo();
|
||||
if (II && II == &S.PP.getIdentifierTable().get(ClassName))
|
||||
*MalformedDecl = D;
|
||||
};
|
||||
|
||||
ClassTemplateDecl *Template = nullptr;
|
||||
const TemplateArgument *Arguments = nullptr;
|
||||
|
||||
QualType Ty = S.Context.getCanonicalType(SugaredType);
|
||||
if (const RecordType *RT = Ty->getAs<RecordType>()) {
|
||||
|
||||
ClassTemplateSpecializationDecl *Specialization =
|
||||
dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
|
||||
if (!Specialization)
|
||||
if (!Specialization) {
|
||||
ReportMatchingNameAsMalformed(RT->getDecl());
|
||||
return false;
|
||||
}
|
||||
|
||||
Template = Specialization->getSpecializedTemplate();
|
||||
Arguments = Specialization->getTemplateArgs().data();
|
||||
@ -12092,91 +12160,146 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
|
||||
Arguments = TST->template_arguments().begin();
|
||||
}
|
||||
}
|
||||
if (!Template)
|
||||
if (!Template) {
|
||||
ReportMatchingNameAsMalformed(Ty->getAsTagDecl());
|
||||
return false;
|
||||
|
||||
if (!StdInitializerList) {
|
||||
// Haven't recognized std::initializer_list yet, maybe this is it.
|
||||
CXXRecordDecl *TemplateClass = Template->getTemplatedDecl();
|
||||
if (TemplateClass->getIdentifier() !=
|
||||
&PP.getIdentifierTable().get("initializer_list") ||
|
||||
!getStdNamespace()->InEnclosingNamespaceSetOf(
|
||||
TemplateClass->getNonTransparentDeclContext()))
|
||||
return false;
|
||||
// This is a template called std::initializer_list, but is it the right
|
||||
// template?
|
||||
TemplateParameterList *Params = Template->getTemplateParameters();
|
||||
if (Params->getMinRequiredArguments() != 1)
|
||||
return false;
|
||||
if (!isa<TemplateTypeParmDecl>(Params->getParam(0)))
|
||||
return false;
|
||||
|
||||
// It's the right template.
|
||||
StdInitializerList = Template;
|
||||
}
|
||||
|
||||
if (Template->getCanonicalDecl() != StdInitializerList->getCanonicalDecl())
|
||||
if (!*CachedDecl) {
|
||||
// Haven't recognized std::{ClassName} yet, maybe this is it.
|
||||
// FIXME: It seems we should just reuse LookupStdClassTemplate but the
|
||||
// semantics of this are slightly different, most notably the existing
|
||||
// "lookup" semantics explicitly diagnose an invalid definition as an
|
||||
// error.
|
||||
CXXRecordDecl *TemplateClass = Template->getTemplatedDecl();
|
||||
if (TemplateClass->getIdentifier() !=
|
||||
&S.PP.getIdentifierTable().get(ClassName) ||
|
||||
!S.getStdNamespace()->InEnclosingNamespaceSetOf(
|
||||
TemplateClass->getNonTransparentDeclContext()))
|
||||
return false;
|
||||
// This is a template called std::{ClassName}, but is it the right
|
||||
// template?
|
||||
TemplateParameterList *Params = Template->getTemplateParameters();
|
||||
if (Params->getMinRequiredArguments() != 1 ||
|
||||
!isa<TemplateTypeParmDecl>(Params->getParam(0))) {
|
||||
if (MalformedDecl)
|
||||
*MalformedDecl = TemplateClass;
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's the right template.
|
||||
*CachedDecl = Template;
|
||||
}
|
||||
|
||||
if (Template->getCanonicalDecl() != (*CachedDecl)->getCanonicalDecl())
|
||||
return false;
|
||||
|
||||
// This is an instance of std::initializer_list. Find the argument type.
|
||||
if (Element)
|
||||
*Element = Arguments[0].getAsType();
|
||||
// This is an instance of std::{ClassName}. Find the argument type.
|
||||
if (TypeArg)
|
||||
*TypeArg = Arguments[0].getAsType();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){
|
||||
NamespaceDecl *Std = S.getStdNamespace();
|
||||
if (!Std) {
|
||||
S.Diag(Loc, diag::err_implied_std_initializer_list_not_found);
|
||||
return nullptr;
|
||||
}
|
||||
bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"Looking for std::initializer_list outside of C++.");
|
||||
|
||||
LookupResult Result(S, &S.PP.getIdentifierTable().get("initializer_list"),
|
||||
Loc, Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Result, Std)) {
|
||||
S.Diag(Loc, diag::err_implied_std_initializer_list_not_found);
|
||||
// We're looking for implicit instantiations of
|
||||
// template <typename E> class std::initializer_list.
|
||||
|
||||
return isStdClassTemplate(*this, Ty, Element, "initializer_list",
|
||||
&StdInitializerList, /*MalformedDecl=*/nullptr);
|
||||
}
|
||||
|
||||
bool Sema::isStdTypeIdentity(QualType Ty, QualType *Element,
|
||||
const Decl **MalformedDecl) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"Looking for std::type_identity outside of C++.");
|
||||
|
||||
// We're looking for implicit instantiations of
|
||||
// template <typename T> struct std::type_identity.
|
||||
|
||||
return isStdClassTemplate(*this, Ty, Element, "type_identity",
|
||||
&StdTypeIdentity, MalformedDecl);
|
||||
}
|
||||
|
||||
static ClassTemplateDecl *LookupStdClassTemplate(Sema &S, SourceLocation Loc,
|
||||
const char *ClassName,
|
||||
bool *WasMalformed) {
|
||||
if (!S.StdNamespace)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LookupResult Result(S, &S.PP.getIdentifierTable().get(ClassName), Loc,
|
||||
Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Result, S.getStdNamespace()))
|
||||
return nullptr;
|
||||
|
||||
ClassTemplateDecl *Template = Result.getAsSingle<ClassTemplateDecl>();
|
||||
if (!Template) {
|
||||
Result.suppressDiagnostics();
|
||||
// We found something weird. Complain about the first thing we found.
|
||||
NamedDecl *Found = *Result.begin();
|
||||
S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list);
|
||||
S.Diag(Found->getLocation(), diag::err_malformed_std_class_template)
|
||||
<< ClassName;
|
||||
if (WasMalformed)
|
||||
*WasMalformed = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We found some template called std::initializer_list. Now verify that it's
|
||||
// We found some template with the correct name. Now verify that it's
|
||||
// correct.
|
||||
TemplateParameterList *Params = Template->getTemplateParameters();
|
||||
if (Params->getMinRequiredArguments() != 1 ||
|
||||
!isa<TemplateTypeParmDecl>(Params->getParam(0))) {
|
||||
S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list);
|
||||
S.Diag(Template->getLocation(), diag::err_malformed_std_class_template)
|
||||
<< ClassName;
|
||||
if (WasMalformed)
|
||||
*WasMalformed = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Template;
|
||||
}
|
||||
|
||||
QualType Sema::BuildStdInitializerList(QualType Element, SourceLocation Loc) {
|
||||
if (!StdInitializerList) {
|
||||
StdInitializerList = LookupStdInitializerList(*this, Loc);
|
||||
if (!StdInitializerList)
|
||||
return QualType();
|
||||
}
|
||||
|
||||
static QualType BuildStdClassTemplate(Sema &S, ClassTemplateDecl *CTD,
|
||||
QualType TypeParam, SourceLocation Loc) {
|
||||
assert(S.getStdNamespace());
|
||||
TemplateArgumentListInfo Args(Loc, Loc);
|
||||
Args.addArgument(TemplateArgumentLoc(TemplateArgument(Element),
|
||||
Context.getTrivialTypeSourceInfo(Element,
|
||||
Loc)));
|
||||
auto TSI = S.Context.getTrivialTypeSourceInfo(TypeParam, Loc);
|
||||
Args.addArgument(TemplateArgumentLoc(TemplateArgument(TypeParam), TSI));
|
||||
|
||||
QualType T = CheckTemplateIdType(TemplateName(StdInitializerList), Loc, Args);
|
||||
QualType T = S.CheckTemplateIdType(TemplateName(CTD), Loc, Args);
|
||||
if (T.isNull())
|
||||
return QualType();
|
||||
|
||||
return Context.getElaboratedType(
|
||||
return S.Context.getElaboratedType(
|
||||
ElaboratedTypeKeyword::None,
|
||||
NestedNameSpecifier::Create(Context, nullptr, getStdNamespace()), T);
|
||||
NestedNameSpecifier::Create(S.Context, nullptr, S.getStdNamespace()), T);
|
||||
}
|
||||
|
||||
QualType Sema::BuildStdInitializerList(QualType Element, SourceLocation Loc) {
|
||||
if (!StdInitializerList) {
|
||||
bool WasMalformed = false;
|
||||
StdInitializerList =
|
||||
LookupStdClassTemplate(*this, Loc, "initializer_list", &WasMalformed);
|
||||
if (!StdInitializerList) {
|
||||
if (!WasMalformed)
|
||||
Diag(Loc, diag::err_implied_std_initializer_list_not_found);
|
||||
return QualType();
|
||||
}
|
||||
}
|
||||
return BuildStdClassTemplate(*this, StdInitializerList, Element, Loc);
|
||||
}
|
||||
|
||||
QualType Sema::tryBuildStdTypeIdentity(QualType Type, SourceLocation Loc) {
|
||||
if (!StdTypeIdentity) {
|
||||
StdTypeIdentity = LookupStdClassTemplate(*this, Loc, "type_identity",
|
||||
/*WasMalformed=*/nullptr);
|
||||
if (!StdTypeIdentity)
|
||||
return QualType();
|
||||
}
|
||||
return BuildStdClassTemplate(*this, StdTypeIdentity, Type, Loc);
|
||||
}
|
||||
|
||||
bool Sema::isInitListConstructor(const FunctionDecl *Ctor) {
|
||||
@ -16288,6 +16411,65 @@ bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
TypeAwareAllocationMode Sema::ShouldUseTypeAwareOperatorNewOrDelete() const {
|
||||
bool SeenTypedOperators = Context.hasSeenTypeAwareOperatorNewOrDelete();
|
||||
return typeAwareAllocationModeFromBool(SeenTypedOperators);
|
||||
}
|
||||
|
||||
FunctionDecl *
|
||||
Sema::BuildTypeAwareUsualDelete(FunctionTemplateDecl *FnTemplateDecl,
|
||||
QualType DeallocType, SourceLocation Loc) {
|
||||
if (DeallocType.isNull())
|
||||
return nullptr;
|
||||
|
||||
FunctionDecl *FnDecl = FnTemplateDecl->getTemplatedDecl();
|
||||
if (!FnDecl->isTypeAwareOperatorNewOrDelete())
|
||||
return nullptr;
|
||||
|
||||
if (FnDecl->isVariadic())
|
||||
return nullptr;
|
||||
|
||||
unsigned NumParams = FnDecl->getNumParams();
|
||||
constexpr unsigned RequiredParameterCount =
|
||||
FunctionDecl::RequiredTypeAwareDeleteParameterCount;
|
||||
// A usual deallocation function has no placement parameters
|
||||
if (NumParams != RequiredParameterCount)
|
||||
return nullptr;
|
||||
|
||||
// A type aware allocation is only usual if the only dependent parameter is
|
||||
// the first parameter.
|
||||
if (llvm::any_of(FnDecl->parameters().drop_front(),
|
||||
[](const ParmVarDecl *ParamDecl) {
|
||||
return ParamDecl->getType()->isDependentType();
|
||||
}))
|
||||
return nullptr;
|
||||
|
||||
QualType SpecializedTypeIdentity = tryBuildStdTypeIdentity(DeallocType, Loc);
|
||||
if (SpecializedTypeIdentity.isNull())
|
||||
return nullptr;
|
||||
|
||||
SmallVector<QualType, RequiredParameterCount> ArgTypes;
|
||||
ArgTypes.reserve(NumParams);
|
||||
|
||||
// The first parameter to a type aware operator delete is by definition the
|
||||
// type-identity argument, so we explicitly set this to the target
|
||||
// type-identity type, the remaining usual parameters should then simply match
|
||||
// the type declared in the function template.
|
||||
ArgTypes.push_back(SpecializedTypeIdentity);
|
||||
for (unsigned ParamIdx = 1; ParamIdx < RequiredParameterCount; ++ParamIdx)
|
||||
ArgTypes.push_back(FnDecl->getParamDecl(ParamIdx)->getType());
|
||||
|
||||
FunctionProtoType::ExtProtoInfo EPI;
|
||||
QualType ExpectedFunctionType =
|
||||
Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
|
||||
sema::TemplateDeductionInfo Info(Loc);
|
||||
FunctionDecl *Result;
|
||||
if (DeduceTemplateArguments(FnTemplateDecl, nullptr, ExpectedFunctionType,
|
||||
Result, Info) != TemplateDeductionResult::Success)
|
||||
return nullptr;
|
||||
return Result;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
CheckOperatorNewDeleteDeclarationScope(Sema &SemaRef,
|
||||
const FunctionDecl *FnDecl) {
|
||||
@ -16317,79 +16499,184 @@ 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();
|
||||
enum class AllocationOperatorKind { New, Delete };
|
||||
|
||||
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);
|
||||
static bool IsPotentiallyTypeAwareOperatorNewOrDelete(Sema &SemaRef,
|
||||
const FunctionDecl *FD,
|
||||
bool *WasMalformed) {
|
||||
const Decl *MalformedDecl = nullptr;
|
||||
if (FD->getNumParams() > 0 &&
|
||||
SemaRef.isStdTypeIdentity(FD->getParamDecl(0)->getType(),
|
||||
/*TypeArgument=*/nullptr, &MalformedDecl))
|
||||
return true;
|
||||
|
||||
if (auto ExpectedPtrTy = ExpectedResultType->getAs<PointerType>())
|
||||
ExpectedResultType = RemoveAddressSpaceFromPtr(SemaRef, ExpectedPtrTy);
|
||||
if (!MalformedDecl)
|
||||
return false;
|
||||
|
||||
if (WasMalformed)
|
||||
*WasMalformed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isDestroyingDeleteT(QualType Type) {
|
||||
auto *RD = Type->getAsCXXRecordDecl();
|
||||
return RD && RD->isInStdNamespace() && RD->getIdentifier() &&
|
||||
RD->getIdentifier()->isStr("destroying_delete_t");
|
||||
}
|
||||
|
||||
static bool IsPotentiallyDestroyingOperatorDelete(Sema &SemaRef,
|
||||
const FunctionDecl *FD) {
|
||||
// C++ P0722:
|
||||
// Within a class C, a single object deallocation function with signature
|
||||
// (T, std::destroying_delete_t, <more params>)
|
||||
// is a destroying operator delete.
|
||||
bool IsPotentiallyTypeAware = IsPotentiallyTypeAwareOperatorNewOrDelete(
|
||||
SemaRef, FD, /*WasMalformed=*/nullptr);
|
||||
unsigned DestroyingDeleteIdx = IsPotentiallyTypeAware + /* address */ 1;
|
||||
return isa<CXXMethodDecl>(FD) && FD->getOverloadedOperator() == OO_Delete &&
|
||||
FD->getNumParams() > DestroyingDeleteIdx &&
|
||||
isDestroyingDeleteT(FD->getParamDecl(DestroyingDeleteIdx)->getType());
|
||||
}
|
||||
|
||||
static inline bool CheckOperatorNewDeleteTypes(
|
||||
Sema &SemaRef, FunctionDecl *FnDecl, AllocationOperatorKind OperatorKind,
|
||||
CanQualType ExpectedResultType, CanQualType ExpectedSizeOrAddressParamType,
|
||||
unsigned DependentParamTypeDiag, unsigned InvalidParamTypeDiag) {
|
||||
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);
|
||||
};
|
||||
|
||||
const unsigned NumParams = FnDecl->getNumParams();
|
||||
unsigned FirstNonTypeParam = 0;
|
||||
bool MalformedTypeIdentity = false;
|
||||
bool IsPotentiallyTypeAware = IsPotentiallyTypeAwareOperatorNewOrDelete(
|
||||
SemaRef, FnDecl, &MalformedTypeIdentity);
|
||||
unsigned MinimumMandatoryArgumentCount = 1;
|
||||
unsigned SizeParameterIndex = 0;
|
||||
if (IsPotentiallyTypeAware) {
|
||||
// We don't emit this diagnosis for template instantiations as we will
|
||||
// have already emitted it for the original template declaration.
|
||||
if (!FnDecl->isTemplateInstantiation()) {
|
||||
unsigned DiagID = SemaRef.getLangOpts().CPlusPlus26
|
||||
? diag::warn_cxx26_type_aware_allocators
|
||||
: diag::ext_cxx26_type_aware_allocators;
|
||||
SemaRef.Diag(FnDecl->getLocation(), DiagID);
|
||||
}
|
||||
|
||||
if (OperatorKind == AllocationOperatorKind::New) {
|
||||
SizeParameterIndex = 1;
|
||||
MinimumMandatoryArgumentCount =
|
||||
FunctionDecl::RequiredTypeAwareNewParameterCount;
|
||||
} else {
|
||||
SizeParameterIndex = 2;
|
||||
MinimumMandatoryArgumentCount =
|
||||
FunctionDecl::RequiredTypeAwareDeleteParameterCount;
|
||||
}
|
||||
FirstNonTypeParam = 1;
|
||||
}
|
||||
|
||||
bool IsPotentiallyDestroyingDelete =
|
||||
IsPotentiallyDestroyingOperatorDelete(SemaRef, FnDecl);
|
||||
|
||||
if (IsPotentiallyDestroyingDelete) {
|
||||
++MinimumMandatoryArgumentCount;
|
||||
++SizeParameterIndex;
|
||||
}
|
||||
|
||||
if (NumParams < MinimumMandatoryArgumentCount)
|
||||
return SemaRef.Diag(FnDecl->getLocation(),
|
||||
diag::err_operator_new_delete_too_few_parameters)
|
||||
<< IsPotentiallyTypeAware << IsPotentiallyDestroyingDelete
|
||||
<< FnDecl->getDeclName() << MinimumMandatoryArgumentCount;
|
||||
|
||||
for (unsigned Idx = 0; Idx < MinimumMandatoryArgumentCount; ++Idx) {
|
||||
const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(Idx);
|
||||
if (ParamDecl->hasDefaultArg())
|
||||
return SemaRef.Diag(FnDecl->getLocation(),
|
||||
diag::err_operator_new_default_arg)
|
||||
<< FnDecl->getDeclName() << Idx << ParamDecl->getDefaultArgRange();
|
||||
}
|
||||
|
||||
auto *FnType = FnDecl->getType()->castAs<FunctionType>();
|
||||
QualType CanResultType = NormalizeType(FnType->getReturnType());
|
||||
QualType CanExpectedResultType = NormalizeType(ExpectedResultType);
|
||||
QualType CanExpectedSizeOrAddressParamType =
|
||||
NormalizeType(ExpectedSizeOrAddressParamType);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// A function template must have at least 2 parameters.
|
||||
if (FnDecl->getDescribedFunctionTemplate() && FnDecl->getNumParams() < 2)
|
||||
if (FnDecl->getDescribedFunctionTemplate() && NumParams < 2)
|
||||
return SemaRef.Diag(FnDecl->getLocation(),
|
||||
diag::err_operator_new_delete_template_too_few_parameters)
|
||||
<< FnDecl->getDeclName();
|
||||
|
||||
// The function decl must have at least 1 parameter.
|
||||
if (FnDecl->getNumParams() == 0)
|
||||
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);
|
||||
}
|
||||
auto CheckType = [&](unsigned ParamIdx, QualType ExpectedType,
|
||||
auto FallbackType) -> bool {
|
||||
const ParmVarDecl *ParamDecl = FnDecl->getParamDecl(ParamIdx);
|
||||
if (ExpectedType.isNull()) {
|
||||
return SemaRef.Diag(FnDecl->getLocation(), InvalidParamTypeDiag)
|
||||
<< IsPotentiallyTypeAware << IsPotentiallyDestroyingDelete
|
||||
<< FnDecl->getDeclName() << (1 + ParamIdx) << FallbackType
|
||||
<< ParamDecl->getSourceRange();
|
||||
}
|
||||
CanQualType CanExpectedTy =
|
||||
NormalizeType(SemaRef.Context.getCanonicalType(ExpectedType));
|
||||
auto ActualParamType =
|
||||
NormalizeType(ParamDecl->getType().getUnqualifiedType());
|
||||
if (ActualParamType == CanExpectedTy)
|
||||
return false;
|
||||
unsigned Diagnostic = ActualParamType->isDependentType()
|
||||
? DependentParamTypeDiag
|
||||
: InvalidParamTypeDiag;
|
||||
return SemaRef.Diag(FnDecl->getLocation(), Diagnostic)
|
||||
<< IsPotentiallyTypeAware << IsPotentiallyDestroyingDelete
|
||||
<< FnDecl->getDeclName() << (1 + ParamIdx) << ExpectedType
|
||||
<< FallbackType << ParamDecl->getSourceRange();
|
||||
};
|
||||
|
||||
// Check that the first parameter type is what we expect.
|
||||
if (SemaRef.Context.getCanonicalType(FirstParamType).getUnqualifiedType() !=
|
||||
ExpectedFirstParamType) {
|
||||
// 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.
|
||||
return SemaRef.Diag(FnDecl->getLocation(), FirstParamType->isDependentType()
|
||||
? DependentParamTypeDiag
|
||||
: InvalidParamTypeDiag)
|
||||
<< FnDecl->getDeclName() << ExpectedFirstParamType;
|
||||
}
|
||||
if (CheckType(FirstNonTypeParam, CanExpectedSizeOrAddressParamType, "size_t"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
FnDecl->setIsDestroyingOperatorDelete(IsPotentiallyDestroyingDelete);
|
||||
|
||||
// If the first parameter type is not a type-identity we're done, otherwise
|
||||
// we need to ensure the size and alignment parameters have the correct type
|
||||
if (!IsPotentiallyTypeAware)
|
||||
return false;
|
||||
|
||||
if (CheckType(SizeParameterIndex, SemaRef.Context.getSizeType(), "size_t"))
|
||||
return true;
|
||||
TypeDecl *StdAlignValTDecl = SemaRef.getStdAlignValT();
|
||||
QualType StdAlignValT =
|
||||
StdAlignValTDecl ? SemaRef.Context.getTypeDeclType(StdAlignValTDecl)
|
||||
: QualType();
|
||||
if (CheckType(SizeParameterIndex + 1, StdAlignValT, "std::align_val_t"))
|
||||
return true;
|
||||
|
||||
FnDecl->setIsTypeAwareOperatorNewOrDelete();
|
||||
return MalformedTypeIdentity;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
|
||||
static bool CheckOperatorNewDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
|
||||
// C++ [basic.stc.dynamic.allocation]p1:
|
||||
// A program is ill-formed if an allocation function is declared in a
|
||||
// namespace scope other than global scope or declared static in global
|
||||
@ -16403,20 +16690,10 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) {
|
||||
// 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))
|
||||
return true;
|
||||
|
||||
// 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();
|
||||
|
||||
return false;
|
||||
return CheckOperatorNewDeleteTypes(
|
||||
SemaRef, FnDecl, AllocationOperatorKind::New, SemaRef.Context.VoidPtrTy,
|
||||
SizeTy, diag::err_operator_new_dependent_param_type,
|
||||
diag::err_operator_new_param_type);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -16429,13 +16706,48 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
|
||||
return true;
|
||||
|
||||
auto *MD = dyn_cast<CXXMethodDecl>(FnDecl);
|
||||
auto ConstructDestroyingDeleteAddressType = [&]() {
|
||||
assert(MD);
|
||||
return SemaRef.Context.getCanonicalType(SemaRef.Context.getPointerType(
|
||||
SemaRef.Context.getRecordType(MD->getParent())));
|
||||
};
|
||||
|
||||
// C++ P2719: A destroying operator delete cannot be type aware
|
||||
// so for QoL we actually check for this explicitly by considering
|
||||
// an destroying-delete appropriate address type and the presence of
|
||||
// any parameter of type destroying_delete_t as an erroneous attempt
|
||||
// to declare a type aware destroying delete, rather than emitting a
|
||||
// pile of incorrect parameter type errors.
|
||||
if (MD && IsPotentiallyTypeAwareOperatorNewOrDelete(
|
||||
SemaRef, MD, /*WasMalformed=*/nullptr)) {
|
||||
QualType AddressParamType =
|
||||
SemaRef.Context.getCanonicalType(MD->getParamDecl(1)->getType());
|
||||
if (AddressParamType != SemaRef.Context.VoidPtrTy &&
|
||||
AddressParamType == ConstructDestroyingDeleteAddressType()) {
|
||||
// The address parameter type implies an author trying to construct a
|
||||
// type aware destroying delete, so we'll see if we can find a parameter
|
||||
// of type `std::destroying_delete_t`, and if we find it we'll report
|
||||
// this as being an attempt at a type aware destroying delete just stop
|
||||
// here. If we don't do this, the resulting incorrect parameter ordering
|
||||
// results in a pile mismatched argument type errors that don't explain
|
||||
// the core problem.
|
||||
for (auto Param : MD->parameters()) {
|
||||
if (isDestroyingDeleteT(Param->getType())) {
|
||||
SemaRef.Diag(MD->getLocation(),
|
||||
diag::err_type_aware_destroying_operator_delete)
|
||||
<< Param->getSourceRange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C++ P0722:
|
||||
// Within a class C, the first parameter of a destroying operator delete
|
||||
// shall be of type C *. The first parameter of any other deallocation
|
||||
// function shall be of type void *.
|
||||
CanQualType ExpectedFirstParamType =
|
||||
MD && MD->isDestroyingOperatorDelete()
|
||||
CanQualType ExpectedAddressParamType =
|
||||
MD && IsPotentiallyDestroyingOperatorDelete(SemaRef, MD)
|
||||
? SemaRef.Context.getCanonicalType(SemaRef.Context.getPointerType(
|
||||
SemaRef.Context.getRecordType(MD->getParent())))
|
||||
: SemaRef.Context.VoidPtrTy;
|
||||
@ -16443,7 +16755,8 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
|
||||
// C++ [basic.stc.dynamic.deallocation]p2:
|
||||
// Each deallocation function shall return void
|
||||
if (CheckOperatorNewDeleteTypes(
|
||||
SemaRef, FnDecl, SemaRef.Context.VoidTy, ExpectedFirstParamType,
|
||||
SemaRef, FnDecl, AllocationOperatorKind::Delete,
|
||||
SemaRef.Context.VoidTy, ExpectedAddressParamType,
|
||||
diag::err_operator_delete_dependent_param_type,
|
||||
diag::err_operator_delete_param_type))
|
||||
return true;
|
||||
@ -16451,11 +16764,12 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2738,6 +2738,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
|
||||
LexicalDC = SemaRef.CurContext;
|
||||
}
|
||||
|
||||
Function->setIsDestroyingOperatorDelete(D->isDestroyingOperatorDelete());
|
||||
Function->setIsTypeAwareOperatorNewOrDelete(
|
||||
D->isTypeAwareOperatorNewOrDelete());
|
||||
Function->setLexicalDeclContext(LexicalDC);
|
||||
|
||||
// Attach the parameters
|
||||
|
@ -1927,6 +1927,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();
|
||||
|
@ -1932,7 +1932,9 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
|
||||
Record.push_back(E->isParenTypeId());
|
||||
|
||||
Record.push_back(E->isGlobalNew());
|
||||
Record.push_back(E->passAlignment());
|
||||
ImplicitAllocationParameters IAP = E->implicitAllocationParameters();
|
||||
Record.push_back(isAlignedAllocation(IAP.PassAlignment));
|
||||
Record.push_back(isTypeAwareAllocation(IAP.PassTypeIdentity));
|
||||
Record.push_back(E->doesUsualArrayDeleteWantSize());
|
||||
Record.push_back(E->CXXNewExprBits.HasInitializer);
|
||||
Record.push_back(E->CXXNewExprBits.StoredInitializationStyle);
|
||||
|
@ -37,7 +37,7 @@ struct G {
|
||||
};
|
||||
|
||||
struct H {
|
||||
template<typename T> void *operator new(T, int); // expected-error {{'operator new' cannot take a dependent type as first parameter; use size_t}}
|
||||
template<typename T> void *operator new(T, int); // expected-error {{'operator new' cannot take a dependent type as its 1st parameter; use size_t}}
|
||||
};
|
||||
|
||||
struct I {
|
||||
|
48
clang/test/CodeGenCXX/Inputs/std-coroutine.h
Normal file
48
clang/test/CodeGenCXX/Inputs/std-coroutine.h
Normal file
@ -0,0 +1,48 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify -fblocks -Wno-unreachable-code -Wno-unused-value
|
||||
#ifndef STD_COROUTINE_H
|
||||
#define STD_COROUTINE_H
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename T> struct remove_reference { typedef T type; };
|
||||
template<typename T> struct remove_reference<T &> { typedef T type; };
|
||||
template<typename T> struct remove_reference<T &&> { typedef T type; };
|
||||
|
||||
template<typename T>
|
||||
typename remove_reference<T>::type &&move(T &&t) noexcept;
|
||||
|
||||
struct input_iterator_tag {};
|
||||
struct forward_iterator_tag : public input_iterator_tag {};
|
||||
|
||||
template <class Ret, typename... T>
|
||||
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
|
||||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {
|
||||
static coroutine_handle from_address(void *) noexcept;
|
||||
static coroutine_handle from_promise(Promise &promise);
|
||||
constexpr void* address() const noexcept;
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
|
||||
static coroutine_handle from_address(void *);
|
||||
constexpr void* address() const noexcept;
|
||||
};
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() noexcept { return false; }
|
||||
void await_suspend(coroutine_handle<>) noexcept {}
|
||||
void await_resume() noexcept {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() noexcept { return true; }
|
||||
void await_suspend(coroutine_handle<>) noexcept {}
|
||||
void await_resume() noexcept {}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // STD_COROUTINE_H
|
212
clang/test/CodeGenCXX/type-aware-allocators.cpp
Normal file
212
clang/test/CodeGenCXX/type-aware-allocators.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fsized-deallocation -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_SIZED_ALIGNED %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_ALIGNED %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_NO_ALIGN %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fsized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -o - -Wno-c++26-extensions | FileCheck --check-prefixes=CHECK,CHECK_SIZED_NO_ALIGN %s
|
||||
// Test default behaviour with c++26
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fsized-deallocation -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck --check-prefixes=CHECK,CHECK_SIZED_ALIGNED %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_ALIGNED %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck --check-prefixes=CHECK,CHECK_NO_SIZE_NO_ALIGN %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fsized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck --check-prefixes=CHECK,CHECK_SIZED_NO_ALIGN %s
|
||||
|
||||
|
||||
namespace std {
|
||||
template <class T> struct type_identity {
|
||||
typedef T type;
|
||||
};
|
||||
enum class align_val_t : __SIZE_TYPE__ {};
|
||||
struct destroying_delete_t {};
|
||||
}
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
|
||||
// Sanity check to esure the semantics of the selected compiler mode
|
||||
// will trigger the exception handlers we are expecting, before
|
||||
// involving type aware allocation.
|
||||
// We duplicate the struct definitions so we don't trigger diagnostics
|
||||
// for changing operator resolution on the same type, and we do the
|
||||
// untyped test before specifying the typed operators rather than using
|
||||
// template constraints so we don't have to deal with monstrous mangling.
|
||||
struct S1 {
|
||||
S1();
|
||||
};
|
||||
|
||||
struct __attribute__((aligned(128))) S2 {
|
||||
S2();
|
||||
};
|
||||
|
||||
struct S3 {
|
||||
S3();
|
||||
};
|
||||
|
||||
struct __attribute__((aligned(128))) S4 {
|
||||
S4();
|
||||
char buffer[130];
|
||||
};
|
||||
|
||||
extern "C" void test_no_type_aware_allocator() {
|
||||
S1 *s1 = new S1;
|
||||
delete s1;
|
||||
S2 *s2 = new S2;
|
||||
delete s2;
|
||||
}
|
||||
// CHECK-LABEL: test_no_type_aware_allocator
|
||||
// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
|
||||
// CHECK: @_ZN2S1C1Ev({{.*}} [[ALLOC_RESULT]])
|
||||
// CHECK-NEXT: unwind label %[[S1LPAD:lpad]]
|
||||
// CHECK_SIZED_ALIGNED: @_ZdlPvm(
|
||||
// CHECK_SIZED_NO_ALIGN: @_ZdlPvm(
|
||||
// CHECK_NO_SIZE_ALIGNED: @_ZdlPv(
|
||||
// CHECK_NO_SIZE_NO_ALIGN: @_ZdlPv(
|
||||
// CHECK_SIZED_ALIGNED: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwmSt11align_val_t(
|
||||
// CHECK_NO_SIZE_ALIGNED: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwmSt11align_val_t(
|
||||
// CHECK_SIZED_NO_ALIGN: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
|
||||
// CHECK_NO_SIZE_NO_ALIGN: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_Znwm(
|
||||
// CHECK: _ZN2S2C1Ev({{.*}} [[ALIGNED_ALLOC_RESULT]])
|
||||
// CHECK-NEXT: unwind label %[[S2LPAD:lpad3]]
|
||||
// CHECK_SIZED_ALIGNED: _ZdlPvmSt11align_val_t(
|
||||
// CHECK_NO_SIZE_ALIGNED: _ZdlPvSt11align_val_t(
|
||||
// CHECK_SIZED_NO_ALIGN: _ZdlPvm(
|
||||
// CHECK_NO_SIZE_NO_ALIGN: _ZdlPv(
|
||||
// CHECK: [[S1LPAD]]:{{.*}};
|
||||
// CHECK_SIZED_ALIGNED: @_ZdlPvm({{.*}}[[ALLOC_RESULT]], {{.*}})
|
||||
// CHECK_SIZED_NO_ALIGN: @_ZdlPvm({{.*}}[[ALLOC_RESULT]], {{.*}})
|
||||
// CHECK_NO_SIZE_ALIGNED: @_ZdlPv({{.*}}[[ALLOC_RESULT]])
|
||||
// CHECK_NO_SIZE_NO_ALIGN: @_ZdlPv({{.*}}[[ALLOC_RESULT]])
|
||||
// CHECK: [[S2LPAD]]:
|
||||
// CHECK_SIZED_ALIGNED: _ZdlPvSt11align_val_t({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
|
||||
// CHECK_NO_SIZE_ALIGNED: _ZdlPvSt11align_val_t({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
|
||||
// CHECK_SIZED_NO_ALIGN: _ZdlPvm({{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}})
|
||||
// CHECK_NO_SIZE_NO_ALIGN: _ZdlPv({{.*}}[[ALIGNED_ALLOC_RESULT]])
|
||||
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
|
||||
|
||||
extern "C" void test_free_type_aware_allocator() {
|
||||
S3 *s3 = new S3;
|
||||
delete s3;
|
||||
S4 *s4 = new S4;
|
||||
delete s4;
|
||||
}
|
||||
// CHECK-LABEL: test_free_type_aware_allocator
|
||||
// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwI2S3EPvSt13type_identityIT_EmSt11align_val_t(
|
||||
// CHECK: @_ZN2S3C1Ev({{.*}}[[ALLOC_RESULT]])
|
||||
// CHECK-NEXT: unwind label %[[S3LPAD:.*]]
|
||||
// CHECK: @_ZdlI2S3EvSt13type_identityIT_EPvmSt11align_val_t(
|
||||
// CHECK: [[ALIGNED_ALLOC_RESULT:%.*]] = call {{.*}} @_ZnwI2S4EPvSt13type_identityIT_EmSt11align_val_t(
|
||||
// CHECK: @_ZN2S4C1Ev({{.*}}[[ALIGNED_ALLOC_RESULT]])
|
||||
// CHECK-NEXT: unwind label %[[S4LPAD:.*]]
|
||||
// CHECK: @_ZdlI2S4EvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}}, {{.*}} 256, {{.*}} 128)
|
||||
// CHECK: [[S3LPAD]]:
|
||||
// CHECK: @_ZdlI2S3EvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}}[[ALLOC_RESULT]], {{.*}} 1, {{.*}} 1)
|
||||
// CHECK: [[S4LPAD]]:
|
||||
// CHECK: @_ZdlI2S4EvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}}[[ALIGNED_ALLOC_RESULT]], {{.*}} 256, {{.*}} 128)
|
||||
|
||||
struct S5 {
|
||||
S5();
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
void operator delete(S5*, std::destroying_delete_t);
|
||||
};
|
||||
|
||||
extern "C" void test_ensure_type_aware_cleanup() {
|
||||
S5 *s5 = new S5;
|
||||
delete s5;
|
||||
}
|
||||
// CHECK-LABEL: test_ensure_type_aware_cleanup
|
||||
// CHECK: [[ALLOC_RESULT:%.*]] = call {{.*}} @_ZN2S5nwIS_EEPvSt13type_identityIT_EmSt11align_val_t(
|
||||
// CHECK: @_ZN2S5C1Ev({{.*}}[[ALLOC_RESULT]])
|
||||
// CHECK-NEXT: unwind label %[[S5LPAD:.*]]
|
||||
// CHECK: @_ZN2S5dlEPS_St19destroying_delete_t(
|
||||
// CHECK: [[S5LPAD]]:
|
||||
// CHECK: @_ZN2S5dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}} [[ALLOC_RESULT]]
|
||||
|
||||
struct S6 {
|
||||
S6();
|
||||
virtual ~S6();
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
S6::~S6(){
|
||||
}
|
||||
// CHECK-LABEL: _ZN2S6D0Ev
|
||||
// CHECK: _ZN2S6dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t(
|
||||
|
||||
struct __attribute__((aligned(128))) S7 : S6 {
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct S8 : S6 {
|
||||
S8();
|
||||
void *operator new(size_t);
|
||||
void operator delete(void*);
|
||||
};
|
||||
|
||||
S8::S8(){}
|
||||
|
||||
extern "C" void test_ensure_type_aware_overrides() {
|
||||
S6 *s6 = new S6;
|
||||
delete s6;
|
||||
S7 *s7 = new S7;
|
||||
delete s7;
|
||||
S8 *s8 = new S8;
|
||||
delete s8;
|
||||
}
|
||||
// CHECK-LABEL: test_ensure_type_aware_overrides
|
||||
// CHECK: [[S6_ALLOC:%.*]] = call {{.*}} @_ZN2S6nwIS_EEPvSt13type_identityIT_EmSt11align_val_t(
|
||||
// CHECK: @_ZN2S6C1Ev({{.*}}[[S6_ALLOC]])
|
||||
// CHECK-NEXT: unwind label %[[S6LPAD:.*]]
|
||||
// CHECK: [[S6_VTABLE:%vtable.*]] = load
|
||||
// CHECK: [[S6_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S6_VTABLE]], i64 1
|
||||
// CHECK: [[S6_DFN:%.*]] = load ptr, ptr [[S6_DFN_ADDR]]
|
||||
// CHECK: call void [[S6_DFN]](
|
||||
// CHECK: [[S7_ALLOC:%.*]] = call {{.*}} @_ZN2S6nwI2S7EEPvSt13type_identityIT_EmSt11align_val_t(
|
||||
// CHECK: @_ZN2S7C1Ev({{.*}}[[S7_ALLOC]])
|
||||
// CHECK-NEXT: unwind label %[[S7LPAD:.*]]
|
||||
// CHECK: [[S7_VTABLE:%vtable.*]] = load
|
||||
// CHECK: [[S7_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S7_VTABLE]], i64 1
|
||||
// CHECK: [[S7_DFN:%.*]] = load ptr, ptr [[S7_DFN_ADDR]]
|
||||
// CHECK: call void [[S7_DFN]](
|
||||
// CHECK: [[S8_ALLOC:%.*]] = call {{.*}} @_ZN2S8nwEm(
|
||||
// CHECK: @_ZN2S8C1Ev({{.*}}[[S8_ALLOC]])
|
||||
// CHECK-NEXT: unwind label %[[S8LPAD:.*]]
|
||||
// CHECK: [[S8_VTABLE:%vtable.*]] = load
|
||||
// CHECK: [[S8_DFN_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[S8_VTABLE]], i64 1
|
||||
// CHECK: [[S8_DFN:%.*]] = load ptr, ptr [[S8_DFN_ADDR]]
|
||||
// CHECK: call void [[S8_DFN]](
|
||||
// CHECK: [[S6LPAD]]:
|
||||
// CHECK: @_ZN2S6dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}} [[S6_ALLOC]], {{.*}}, {{.*}})
|
||||
// CHECK: [[S7LPAD]]:
|
||||
// CHECK: @_ZN2S6dlI2S7EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}}, {{.*}} [[S7_ALLOC]], {{.*}}, {{.*}})
|
||||
// CHECK: [[S8LPAD]]:
|
||||
// CHECK: @_ZN2S8dlEPv({{.*}} [[S8_ALLOC]])
|
||||
|
||||
struct __attribute__((aligned(128))) S11 {
|
||||
S11();
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
void operator delete(S11*, std::destroying_delete_t, std::align_val_t);
|
||||
};
|
||||
|
||||
|
||||
struct S12 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, unsigned line = __builtin_LINE());
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, unsigned line = __builtin_LINE());
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
extern "C" void test_ensure_type_aware_resolution_includes_location() {
|
||||
S12 *s12 = new S12(); // test line
|
||||
delete s12;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: test_ensure_type_aware_resolution_includes_location
|
||||
// `180` in the next line is the line number from the test line in above
|
||||
// CHECK: %call = call noundef ptr @_ZN3S12nwIS_EEPvSt13type_identityIT_EmSt11align_val_tj({{.*}}, {{.*}}, {{.*}}, {{.*}})
|
||||
|
||||
// CHECK-LABEL: @_ZN2S8D0Ev
|
||||
// CHECK: @_ZN2S8dlEPv(
|
||||
|
||||
// CHECK-LABEL: _ZN2S7D0Ev
|
||||
// CHECK: _ZN2S6dlI2S7EEvSt13type_identityIT_EPvmSt11align_val_t(
|
128
clang/test/CodeGenCXX/type-aware-coroutines.cpp
Normal file
128
clang/test/CodeGenCXX/type-aware-coroutines.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx %s -std=c++23 -fcoroutines -fexceptions -emit-llvm -Wno-coro-type-aware-allocation-function -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx %s -std=c++26 -fcoroutines -fexceptions -emit-llvm -Wno-coro-type-aware-allocation-function -o - | FileCheck %s
|
||||
|
||||
#include "Inputs/std-coroutine.h"
|
||||
|
||||
namespace std {
|
||||
template <typename T> struct type_identity {
|
||||
typedef T type;
|
||||
};
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
enum class align_val_t {};
|
||||
}
|
||||
|
||||
struct Allocator {};
|
||||
|
||||
struct resumable {
|
||||
struct promise_type {
|
||||
promise_type();
|
||||
void *operator new(std::size_t sz, int);
|
||||
void *operator new(std::size_t sz, float);
|
||||
void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, int);
|
||||
void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float);
|
||||
void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t) = delete;
|
||||
void operator delete(void *);
|
||||
|
||||
resumable get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
|
||||
struct resumable2 {
|
||||
struct promise_type {
|
||||
promise_type();
|
||||
template <typename... Args> void *operator new(std::size_t sz, Args...);
|
||||
template <typename... Args> void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, Args...);
|
||||
void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t);
|
||||
void operator delete(void *);
|
||||
|
||||
resumable2 get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
|
||||
// CHECK-LABEL: void @f1
|
||||
extern "C" resumable f1(int) {
|
||||
co_return;
|
||||
// CHECK: coro.alloc:
|
||||
// CHECK: _ZN9resumable12promise_typenwEmi
|
||||
// CHECK: coro.free:
|
||||
// CHECK: _ZN9resumable12promise_typedlEPv
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @f2
|
||||
extern "C" resumable f2(float) {
|
||||
co_return;
|
||||
// CHECK: coro.alloc:
|
||||
// CHECK: _ZN9resumable12promise_typenwEmf
|
||||
// CHECK: coro.free:
|
||||
// CHECK: _ZN9resumable12promise_typedlEPv
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @f3
|
||||
extern "C" resumable2 f3(int, float, const char*, Allocator) {
|
||||
co_yield 1;
|
||||
co_return;
|
||||
// CHECK: coro.alloc:
|
||||
// CHECK: _ZN10resumable212promise_typenwIJifPKc9AllocatorEEEPvmDpT_
|
||||
// CHECK: coro.free:
|
||||
// CHECK: _ZN10resumable212promise_typedlEPv
|
||||
// CHECK: _ZN10resumable212promise_typedlEPv
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @f4
|
||||
extern "C" resumable f4(int n = 10) {
|
||||
for (int i = 0; i < n; i++) co_yield i;
|
||||
// CHECK: coro.alloc:
|
||||
// CHECK: call {{.*}}@_ZN9resumable12promise_typenwEmi(
|
||||
// CHECK: coro.free:
|
||||
// CHECK: call void @_ZN9resumable12promise_typedlEPv(
|
||||
// CHECK: call void @_ZN9resumable12promise_typedlEPv(
|
||||
}
|
||||
|
||||
struct resumable3 {
|
||||
struct promise_type {
|
||||
promise_type();
|
||||
resumable3 get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
template <typename T> void *operator new(std::type_identity<T>, std::size_t sz, std::align_val_t);
|
||||
template <typename T, typename... Args> void *operator new(std::type_identity<T>, std::size_t sz, std::align_val_t, Args...);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t);
|
||||
template <typename T, typename... Args> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t, Args...);
|
||||
|
||||
// CHECK-LABEL: void @f5
|
||||
extern "C" resumable3 f5(float) {
|
||||
co_return;
|
||||
// CHECK: coro.alloc:
|
||||
// CHECK: call {{.*}}@_Znwm(
|
||||
// CHECK: coro.free:
|
||||
// CHECK: call void @_ZdlPvm(
|
||||
// CHECK: call void @_ZdlPvm(
|
||||
}
|
||||
|
||||
// CHECK-LABEL: void @f4.resume
|
||||
// CHECK: coro.free:
|
||||
// CHECK: _ZN9resumable12promise_typedlEPv
|
||||
|
||||
// CHECK-LABEL: void @f4.destroy
|
||||
// CHECK: coro.free:
|
||||
// CHECK: _ZN9resumable12promise_typedlEPv
|
||||
|
||||
// CHECK-LABEL: void @f4.cleanup
|
||||
// CHECK: coro.free:
|
||||
// CHECK: _ZN9resumable12promise_typedlEPv
|
56
clang/test/CodeGenCXX/type-aware-new-constexpr.cpp
Normal file
56
clang/test/CodeGenCXX/type-aware-new-constexpr.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fsized-deallocation -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fno-sized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -fsized-deallocation -fno-aligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -faligned-allocation -emit-llvm -fcxx-exceptions -fexceptions -std=c++26 -fexperimental-new-constant-interpreter -o - | FileCheck %s
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
|
||||
namespace std {
|
||||
template <class T> struct type_identity {
|
||||
typedef T type;
|
||||
};
|
||||
enum class align_val_t : size_t {};
|
||||
}
|
||||
|
||||
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
|
||||
struct S {
|
||||
int i = 0;
|
||||
constexpr S() __attribute__((noinline)) {}
|
||||
};
|
||||
|
||||
constexpr int doSomething() {
|
||||
S* s = new S;
|
||||
int result = s->i;
|
||||
delete s;
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr int force_doSomething = doSomething();
|
||||
template <int N> struct Tag {};
|
||||
|
||||
void test1(Tag<force_doSomething>){
|
||||
// CHECK-LABEL: define void @_Z5test13TagILi0EE
|
||||
}
|
||||
|
||||
void test2(Tag<doSomething() + 1>){
|
||||
// CHECK-LABEL: define void @_Z5test23TagILi1EE
|
||||
}
|
||||
|
||||
int main() {
|
||||
// CHECK-LABEL: define noundef i32 @main()
|
||||
return doSomething();
|
||||
// CHECK: call{{.*}}i32 @_Z11doSomethingv()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define linkonce_odr noundef i32 @_Z11doSomethingv()
|
||||
// CHECK: [[ALLOC:%.*]] = call noundef ptr @_ZnwI1SEPvSt13type_identityIT_Em
|
||||
// CHECK: invoke noundef ptr @_ZN1SC1Ev(ptr{{.*}} [[ALLOC]])
|
||||
// CHECK-NEXT: to label %[[CONT:.*]] unwind label %[[LPAD:[a-z0-9]+]]
|
||||
// CHECK: [[CONT]]:
|
||||
// CHECK: call void @_ZdlI1SEvSt13type_identityIT_EPv
|
||||
// CHECK: ret
|
||||
// CHECK: [[LPAD]]:
|
||||
// call void @_ZdlI1SEvSt13type_identityIT_EPvmSt11align_val_t({{.*}} [[ALLOC]])
|
75
clang/test/CodeGenCXX/type-aware-placement-operators.cpp
Normal file
75
clang/test/CodeGenCXX/type-aware-placement-operators.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -fsized-deallocation -faligned-allocation -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -fno-sized-deallocation -faligned-allocation -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -fsized-deallocation -fno-aligned-allocation -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple arm64-apple-macosx -emit-llvm -fcxx-exceptions -fexceptions -std=c++23 -fno-aligned-allocation -fno-sized-deallocation -o - | FileCheck %s
|
||||
|
||||
namespace std {
|
||||
template <class T> struct type_identity {};
|
||||
enum class align_val_t : __SIZE_TYPE__ {};
|
||||
}
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
struct Context;
|
||||
struct S1 {
|
||||
S1();
|
||||
int i;
|
||||
};
|
||||
|
||||
void *operator new(std::type_identity<S1>, size_t, std::align_val_t, Context&);
|
||||
void operator delete(std::type_identity<S1>, void*, size_t, std::align_val_t, Context&); // #1
|
||||
void operator delete(std::type_identity<S1>, void*, size_t, std::align_val_t);
|
||||
|
||||
struct S2 {
|
||||
S2();
|
||||
int i;
|
||||
template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&);
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Context&); // #2
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #3
|
||||
};
|
||||
|
||||
struct S3 {
|
||||
S3();
|
||||
int i;
|
||||
template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #4
|
||||
};
|
||||
|
||||
extern "C" void test_s1(Context& Ctx) {
|
||||
S1 *s1 = new (Ctx) S1;
|
||||
delete s1;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: test_s1
|
||||
// CHECK: [[S1_NEW:%.*]] = call noundef ptr @_ZnwSt13type_identityI2S1EmSt11align_val_tR7Context({{.*}},{{.*}} 4,{{.*}} 4, ptr noundef nonnull align 1 [[S1_NEW_CONTEXT:%.*]])
|
||||
// CHECK: call void @_ZdlSt13type_identityI2S1EPvmSt11align_val_t({{.*}}, ptr noundef %2,{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: call void @_ZdlSt13type_identityI2S1EPvmSt11align_val_tR7Context({{.*}}, ptr noundef [[S1_NEW]],{{.*}} 4,{{.*}} 4, ptr noundef nonnull align 1 [[S1_NEW_CONTEXT]])
|
||||
|
||||
extern "C" void test_s2(Context& Ctx) {
|
||||
S2 *s2_1 = new (Ctx) S2;
|
||||
delete s2_1;
|
||||
S2 *s2_2 = new (std::align_val_t(128), Ctx) S2;
|
||||
delete s2_2;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: test_s2
|
||||
// CHECK: [[S2_NEW1:%.*]] = call noundef ptr @_ZN2S2nwIS_EEPvSt13type_identityIT_EmSt11align_val_tR7Context({{.*}},{{.*}} 4,{{.*}} 4,{{.*}} [[S2_NEW1_CONTEXT:%.*]])
|
||||
// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: [[S2_NEW2:%.*]] = call noundef ptr @_ZN2S2nwIS_EEPvSt13type_identityIT_EmSt11align_val_tR7Context({{.*}},{{.*}} 4,{{.*}} 128,{{.*}} [[S2_NEW2_CONTEXT:%.*]])
|
||||
// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_tR7Context({{.*}}, {{.*}} [[S2_NEW1]],{{.*}} 4,{{.*}} 4,{{.*}} [[S2_NEW1_CONTEXT]])
|
||||
// CHECK: call void @_ZN2S2dlIS_EEvSt13type_identityIT_EPvmSt11align_val_tR7Context({{.*}}, {{.*}} [[S2_NEW2]],{{.*}} 4,{{.*}} 128,{{.*}} [[S2_NEW2_CONTEXT]])
|
||||
|
||||
extern "C" void test_s3(Context& Ctx) {
|
||||
S3 *s3_1 = new S3;
|
||||
delete s3_1;
|
||||
S3 *s3_2 = new (std::align_val_t(128)) S3;
|
||||
delete s3_2;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: test_s3
|
||||
// CHECK: [[S3_NEW1:%.*]] = call noundef ptr @_ZN2S3nwIS_EEPvSt13type_identityIT_EmSt11align_val_t({{.*}},{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: [[S3_NEW2:%.*]] = call noundef ptr @_ZN2S3nwIS_EEPvSt13type_identityIT_EmSt11align_val_t({{.*}},{{.*}} 4,{{.*}} 128)
|
||||
// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}},{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}}[[S3_NEW1]],{{.*}} 4,{{.*}} 4)
|
||||
// CHECK: call void @_ZN2S3dlIS_EEvSt13type_identityIT_EPvmSt11align_val_t({{.*}},{{.*}}[[S3_NEW2]],{{.*}} 4,{{.*}} 128)
|
@ -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++26 -triple x86_64 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
|
||||
|
||||
#include "Inputs/coroutine.h"
|
||||
|
||||
namespace std {
|
||||
|
@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 -fmodules -verify %s
|
||||
// RUN: %clang_cc1 -fmodules -std=c++26 -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
#pragma clang module build M
|
||||
|
@ -16,7 +16,7 @@ namespace std {
|
||||
void operator delete(void*, std::destroying_delete_t); // ok, just a placement delete
|
||||
|
||||
struct A;
|
||||
void operator delete(A*, std::destroying_delete_t); // expected-error {{first parameter of 'operator delete' must have type 'void *'}}
|
||||
void operator delete(A*, std::destroying_delete_t); // expected-error {{1st parameter of 'operator delete' must have type 'void *'}}
|
||||
|
||||
struct A {
|
||||
void operator delete(A*, std::destroying_delete_t);
|
||||
@ -27,7 +27,7 @@ struct A {
|
||||
// FIXME: It's probably a language defect that we permit usual operator delete to be variadic.
|
||||
void operator delete(A*, std::destroying_delete_t, std::size_t, ...);
|
||||
|
||||
void operator delete(struct X*, std::destroying_delete_t, std::size_t, ...); // expected-error {{first parameter of 'operator delete' must have type 'A *'}}
|
||||
void operator delete(struct X*, std::destroying_delete_t, std::size_t, ...); // expected-error {{1st parameter of destroying 'operator delete' must have type 'A *'}}
|
||||
|
||||
void operator delete(void*, std::size_t);
|
||||
};
|
||||
@ -191,7 +191,7 @@ namespace delete_from_new {
|
||||
namespace GH96191 {
|
||||
struct S {};
|
||||
struct T {
|
||||
void operator delete(S) { } // expected-error {{first parameter of 'operator delete' must have type 'void *'}}
|
||||
void operator delete(S) { } // expected-error {{1st parameter of 'operator delete' must have type 'void *'}}
|
||||
};
|
||||
|
||||
void foo(T *t) { delete t; }
|
||||
|
@ -5,6 +5,10 @@
|
||||
// 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}:"[]"
|
||||
|
@ -251,7 +251,7 @@ void loadEngineFor() {
|
||||
}
|
||||
|
||||
template <class T> struct TBase {
|
||||
void* operator new(T size, int); // expected-error {{'operator new' cannot take a dependent type as first parameter; use size_t}}
|
||||
void* operator new(T size, int); // expected-error {{'operator new' cannot take a dependent type as its 1st parameter; use size_t}}
|
||||
};
|
||||
|
||||
TBase<int> t1;
|
||||
@ -466,7 +466,7 @@ namespace TemplateDestructors {
|
||||
|
||||
namespace DeleteParam {
|
||||
struct X {
|
||||
void operator delete(X*); // expected-error{{first parameter of 'operator delete' must have type 'void *'}}
|
||||
void operator delete(X*); // expected-error{{1st parameter of 'operator delete' must have type 'void *'}}
|
||||
};
|
||||
|
||||
struct Y {
|
||||
|
@ -0,0 +1,261 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fsized-deallocation -faligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=1
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation -faligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=0
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fsized-deallocation -fno-aligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=1
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation -fno-aligned-allocation -Wno-non-c-typedef-for-linkage -DDEFAULT_DELETE=0
|
||||
|
||||
namespace std {
|
||||
template <class T> struct type_identity {};
|
||||
enum class align_val_t : __SIZE_TYPE__ {};
|
||||
}
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
|
||||
void *operator new(size_t); // #default_operator_new
|
||||
|
||||
#if DEFAULT_DELETE==0
|
||||
void operator delete(void*) noexcept; // #default_operator_delete
|
||||
#elif DEFAULT_DELETE==1
|
||||
void operator delete(void*, size_t) noexcept; // #default_operator_delete
|
||||
#elif DEFAULT_DELETE==2
|
||||
void operator delete(void*, std::align_val_t) noexcept; // #default_operator_delete
|
||||
#elif DEFAULT_DELETE==3
|
||||
void operator delete(void*, size_t, std::align_val_t) noexcept; // #default_operator_delete
|
||||
#endif
|
||||
|
||||
struct Invalid1 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in 'Invalid1' must have matching type aware 'operator delete'}}
|
||||
void *operator new(std::type_identity<Invalid1>, size_t, std::align_val_t);
|
||||
// expected-note@-1 {{unmatched type aware 'operator new' declared here}}
|
||||
};
|
||||
struct Invalid2 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in 'Invalid2' must have matching type aware 'operator delete'}}
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #Invalid2_new
|
||||
// expected-note@-1 {{unmatched type aware 'operator new' declared here}}
|
||||
};
|
||||
struct Invalid3 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in 'Invalid3' must have matching type aware 'operator new'}}
|
||||
void operator delete(std::type_identity<Invalid3>, void*, size_t, std::align_val_t);
|
||||
// expected-note@-1 {{unmatched type aware 'operator delete' declared here}}
|
||||
};
|
||||
struct Invalid4 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in 'Invalid4' must have matching type aware 'operator new'}}
|
||||
template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #Invalid4_delete
|
||||
// expected-note@-1 {{unmatched type aware 'operator delete' declared here}}
|
||||
};
|
||||
struct Invalid5 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new[]' in 'Invalid5' must have matching type aware 'operator delete[]'}}
|
||||
void *operator new[](std::type_identity<Invalid5>, size_t, std::align_val_t);
|
||||
// expected-note@-1 {{unmatched type aware 'operator new[]' declared here}}
|
||||
};
|
||||
struct Invalid6 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new[]' in 'Invalid6' must have matching type aware 'operator delete[]'}}
|
||||
template <class T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t);
|
||||
// expected-note@-1 {{unmatched type aware 'operator new[]' declared here}}
|
||||
};
|
||||
struct Invalid7 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete[]' in 'Invalid7' must have matching type aware 'operator new[]'}}
|
||||
void operator delete[](std::type_identity<Invalid7>, void*, size_t, std::align_val_t);
|
||||
// expected-note@-1 {{unmatched type aware 'operator delete[]' declared here}}
|
||||
};
|
||||
struct Invalid8 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete[]' in 'Invalid8' must have matching type aware 'operator new[]'}}
|
||||
template <class T> void operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
// expected-note@-1 {{unmatched type aware 'operator delete[]' declared here}}
|
||||
};
|
||||
|
||||
// Invalid9 and Invalid10 will ensure we report the correct owner for the
|
||||
// resolved, but unmatched, new and delete
|
||||
struct Invalid9: Invalid2 {};
|
||||
struct Invalid10: Invalid4 {};
|
||||
// Invalid11 inherits a "matching" new and delete pair (so no inheritance ambiguity)
|
||||
// but the resolved operators are from different scopes
|
||||
struct Invalid11 : Invalid2, Invalid4 {};
|
||||
struct Invalid12 : Invalid2, Invalid4 {
|
||||
using Invalid2::operator new;
|
||||
using Invalid4::operator delete;
|
||||
};
|
||||
|
||||
struct TestClass1 {
|
||||
void *operator new(std::type_identity<TestClass1>, size_t, std::align_val_t); // #TestClass1_new
|
||||
void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #TestClass1_delete
|
||||
};
|
||||
|
||||
struct TestClass2 {
|
||||
void *operator new(std::type_identity<int>, size_t, std::align_val_t); // #TestClass2_new
|
||||
void operator delete(std::type_identity<TestClass2>, void *, size_t, std::align_val_t); // #TestClass2_delete
|
||||
};
|
||||
|
||||
void basic_tests() {
|
||||
TestClass1 * tc1 = new TestClass1;
|
||||
delete tc1;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'TestClass1'}}
|
||||
// expected-note@#TestClass1_delete {{member 'operator delete' declared here}}
|
||||
TestClass2 * tc2 = new TestClass2;
|
||||
// expected-error@-1 {{no matching function for call to 'operator new'}}
|
||||
delete tc2;
|
||||
Invalid9 * i9 = new Invalid9;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
|
||||
delete i9;
|
||||
Invalid10 * i10 = new Invalid10;
|
||||
// expected-error@-1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
|
||||
// expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
|
||||
// expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
|
||||
delete i10;
|
||||
Invalid11 * i11 = new Invalid11;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
|
||||
// expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
|
||||
delete i11;
|
||||
Invalid12 * i12 = new Invalid12;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
|
||||
// expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
|
||||
delete i12;
|
||||
}
|
||||
|
||||
struct Baseclass1 {
|
||||
void *operator new(std::type_identity<Baseclass1>, size_t, std::align_val_t);
|
||||
void operator delete(std::type_identity<Baseclass1>, void *, size_t, std::align_val_t); // #Baseclass1_delete
|
||||
};
|
||||
|
||||
struct Subclass1 : Baseclass1 {
|
||||
Subclass1();
|
||||
};
|
||||
|
||||
struct Baseclass2 {
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
void operator delete(std::type_identity<Baseclass2>, void *, size_t, std::align_val_t); // #Baseclass2_delete
|
||||
};
|
||||
|
||||
struct Subclass2 : Baseclass2 {
|
||||
Subclass2();
|
||||
};
|
||||
|
||||
struct Baseclass3 {
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <class T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
struct Subclass3_1 : Baseclass3 {
|
||||
Subclass3_1();
|
||||
void *operator new(std::type_identity<int>, size_t, std::align_val_t);
|
||||
template <class T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
struct Subclass3_2 : Baseclass3 {
|
||||
Subclass3_2();
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #Subclass3_2_delete
|
||||
};
|
||||
|
||||
|
||||
void test_subclasses() {
|
||||
Subclass1 * sc1 = new Subclass1;
|
||||
// expected-error@-1 {{no matching function for call to 'operator new'}}
|
||||
delete sc1;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'Subclass1'}}
|
||||
// expected-note@#Baseclass1_delete {{member 'operator delete' declared here}}
|
||||
Subclass2 * sc2 = new Subclass2;
|
||||
delete sc2;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'Subclass2'}}
|
||||
// expected-note@#Baseclass2_delete {{member 'operator delete' declared here}}
|
||||
Subclass3_1 * sc3_1 = new Subclass3_1;
|
||||
// expected-error@-1 {{no matching function for call to 'operator new'}}
|
||||
delete sc3_1;
|
||||
Subclass3_2 * sc3_2 = new Subclass3_2;
|
||||
delete sc3_2;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'Subclass3_2'}}
|
||||
// expected-note@#Subclass3_2_delete {{member 'operator delete' declared here}}
|
||||
}
|
||||
|
||||
template <class A, class B> constexpr bool same_type_v = false;
|
||||
template <class A> constexpr bool same_type_v<A, A> = true;
|
||||
|
||||
template <class T> struct InvalidConstrainedOperator {
|
||||
template <class U> void *operator new(std::type_identity<U>, size_t, std::align_val_t);
|
||||
template <class U> requires (same_type_v<T, int>) void operator delete(std::type_identity<U>, void *, size_t, std::align_val_t); // #InvalidConstrainedOperator_delete
|
||||
};
|
||||
|
||||
struct Context;
|
||||
template <class T> struct InvalidConstrainedCleanup {
|
||||
template <class U> void *operator new(std::type_identity<U>, size_t, std::align_val_t, Context&); // #InvalidConstrainedCleanup_placement_new
|
||||
template <class U> requires (same_type_v<T, int>) void operator delete(std::type_identity<U>, void *, size_t, std::align_val_t, Context&); // #InvalidConstrainedCleanup_delete
|
||||
template <class U> void operator delete(std::type_identity<U>, void *, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
void test_incompatible_constrained_operators(Context &Ctx) {
|
||||
InvalidConstrainedOperator<int> *ico1 = new InvalidConstrainedOperator<int>;
|
||||
delete ico1;
|
||||
InvalidConstrainedOperator<float> *ico2 = new InvalidConstrainedOperator<float>;
|
||||
delete ico2;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'InvalidConstrainedOperator<float>'}}
|
||||
// expected-note@#InvalidConstrainedOperator_delete {{member 'operator delete' declared here}}
|
||||
InvalidConstrainedCleanup<int> *icc1 = new (Ctx) InvalidConstrainedCleanup<int>;
|
||||
delete icc1;
|
||||
InvalidConstrainedCleanup<float> *icc2 = new (Ctx) InvalidConstrainedCleanup<float>;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#InvalidConstrainedCleanup_placement_new {{type aware 'operator new' declared here in 'InvalidConstrainedCleanup<float>'}}
|
||||
delete icc2;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in '(unnamed struct}}
|
||||
// expected-note@#AnonymousClass1_new {{unmatched type aware 'operator new' declared here}}
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #AnonymousClass1_new
|
||||
} AnonymousClass1;
|
||||
|
||||
typedef struct {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in '(unnamed struct}}
|
||||
template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #AnonymousClass2_delete
|
||||
} AnonymousClass2;
|
||||
|
||||
typedef struct {
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
} AnonymousClass3;
|
||||
|
||||
using AnonymousClass4 = struct {};
|
||||
using AnonymousClass5 = struct {};
|
||||
using AnonymousClass6 = struct {};
|
||||
using AnonymousClass7 = struct {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in}}
|
||||
// expected-note@#AnonymousClass7_new {{unmatched type aware 'operator new' declared here}}
|
||||
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&); // #AnonymousClass7_new
|
||||
};
|
||||
|
||||
|
||||
void *operator new(std::type_identity<AnonymousClass4>, size_t, std::align_val_t); // #AnonymousClass4_new
|
||||
void operator delete(std::type_identity<AnonymousClass5>, void*, size_t, std::align_val_t); // #AnonymousClass5_delete
|
||||
void *operator new(std::type_identity<AnonymousClass6>, size_t, std::align_val_t, Context&); // #AnonymousClass6_placement_new
|
||||
void operator delete(std::type_identity<AnonymousClass6>, void*, size_t, std::align_val_t);
|
||||
void test_anonymous_types(Context &Ctx) {
|
||||
AnonymousClass1 *ac1 = new AnonymousClass1;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#AnonymousClass1_new {{type aware 'operator new' declared here in 'AnonymousClass1'}}
|
||||
|
||||
delete ac1;
|
||||
AnonymousClass2 *ac2 = new AnonymousClass2;
|
||||
// expected-error@-1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
|
||||
// expected-note@#AnonymousClass2_delete {{unmatched type aware 'operator delete' declared here}}
|
||||
// expected-note@#AnonymousClass2_delete {{type aware 'operator delete' declared here in 'AnonymousClass2'}}
|
||||
// expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
|
||||
|
||||
delete ac2;
|
||||
AnonymousClass3 *ac3 = new AnonymousClass3;
|
||||
delete ac3;
|
||||
AnonymousClass4 *ac4 = new AnonymousClass4;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#AnonymousClass4_new {{type aware 'operator new' declared here in the global namespace}}
|
||||
delete ac4;
|
||||
AnonymousClass5 *ac5 = new AnonymousClass5;
|
||||
// expected-error@-1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
|
||||
// expected-note@#AnonymousClass5_delete {{type aware 'operator delete' declared here}}
|
||||
// expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
|
||||
delete ac5;
|
||||
AnonymousClass6 *ac6 = new (Ctx) AnonymousClass6;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#AnonymousClass6_placement_new {{type aware 'operator new' declared here in the global namespace}}
|
||||
// expected-note@#default_operator_delete {{non-type aware 'operator delete' declared here in the global namespace}}
|
||||
delete ac6;
|
||||
}
|
145
clang/test/SemaCXX/type-aware-coroutines.cpp
Normal file
145
clang/test/SemaCXX/type-aware-coroutines.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fcoroutines -fexceptions -Wall -Wpedantic
|
||||
|
||||
|
||||
#include "Inputs/std-coroutine.h"
|
||||
|
||||
namespace std {
|
||||
template <typename T> struct type_identity {
|
||||
typedef T type;
|
||||
};
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
enum class align_val_t : size_t {};
|
||||
}
|
||||
|
||||
struct Allocator {};
|
||||
|
||||
struct resumable {
|
||||
struct promise_type {
|
||||
void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, int); // #resumable_tan1
|
||||
void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float); // #resumable_tan2
|
||||
void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t); // #resumable_tad1
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t) = delete; // #resumable_tad2
|
||||
|
||||
resumable get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
|
||||
struct resumable2 {
|
||||
struct promise_type {
|
||||
template <typename... Args> void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, Args...); // #resumable2_tan1
|
||||
void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t); // #resumable2_tad2
|
||||
|
||||
resumable2 get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
struct resumable3 {
|
||||
struct promise_type {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in 'resumable3::promise_type' must have matching type aware 'operator delete'}}
|
||||
// expected-note@#resumable3_tan {{unmatched type aware 'operator new' declared here}}
|
||||
void *operator new(std::size_t sz, float);
|
||||
void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float); // #resumable3_tan
|
||||
void operator delete(void *);
|
||||
|
||||
resumable3 get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
struct resumable4 {
|
||||
struct promise_type {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in 'resumable4::promise_type' must have matching type aware 'operator new'}}
|
||||
// expected-note@#resumable4_tad {{unmatched type aware 'operator delete' declared here}}
|
||||
void *operator new(std::size_t sz, float);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable4_tad
|
||||
|
||||
resumable4 get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
struct resumable5 {
|
||||
struct promise_type {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in 'resumable5::promise_type' must have matching type aware 'operator new'}}
|
||||
// expected-note@#resumable5_tad {{unmatched type aware 'operator delete' declared here}}
|
||||
void *operator new(std::size_t sz, float);
|
||||
void operator delete(void *);
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable5_tad
|
||||
|
||||
resumable5 get_return_object() { return {}; }
|
||||
auto initial_suspend() { return std::suspend_always(); }
|
||||
auto final_suspend() noexcept { return std::suspend_always(); }
|
||||
void unhandled_exception() {}
|
||||
void return_void(){};
|
||||
std::suspend_always yield_value(int i);
|
||||
};
|
||||
};
|
||||
|
||||
resumable f1(int) {
|
||||
// expected-error@-1 {{'operator new' provided by 'std::coroutine_traits<resumable, int>::promise_type' (aka 'resumable::promise_type') is not usable with the function signature of 'f1'}}
|
||||
// expected-note@-2 {{type aware 'operator new' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable_tan1 {{type aware 'operator new' declared here}}
|
||||
// expected-note@#resumable_tan2 {{type aware 'operator new' declared here}}
|
||||
co_return;
|
||||
}
|
||||
|
||||
resumable f2(float) {
|
||||
// expected-error@-1 {{'operator new' provided by 'std::coroutine_traits<resumable, float>::promise_type' (aka 'resumable::promise_type') is not usable with the function signature of 'f2'}}
|
||||
// expected-note@-2 {{type aware 'operator new' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable_tan1 {{type aware 'operator new' declared here}}
|
||||
// expected-note@#resumable_tan2 {{type aware 'operator new' declared here}}
|
||||
co_return;
|
||||
}
|
||||
|
||||
resumable2 f3(int, float, const char*, Allocator) {
|
||||
// expected-error@-1 {{'operator new' provided by 'std::coroutine_traits<resumable2, int, float, const char *, Allocator>::promise_type' (aka 'resumable2::promise_type') is not usable with the function signature of 'f3'}}
|
||||
// expected-note@-2 {{type aware 'operator new' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable2_tan1 {{type aware 'operator new' declared here}}
|
||||
co_yield 1;
|
||||
co_return;
|
||||
}
|
||||
|
||||
resumable f4(int n = 10) {
|
||||
// expected-error@-1 {{'operator new' provided by 'std::coroutine_traits<resumable, int>::promise_type' (aka 'resumable::promise_type') is not usable with the function signature of 'f4'}}
|
||||
// expected-note@-2 {{type aware 'operator new' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable_tan1 {{type aware 'operator new' declared here}}
|
||||
// expected-note@#resumable_tan2 {{type aware 'operator new' declared here}}
|
||||
for (int i = 0; i < n; i++)
|
||||
co_yield i;
|
||||
}
|
||||
resumable3 f5(float) {
|
||||
// expected-warning@-1 {{type aware 'operator new' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable3_tan {{type aware 'operator new' declared here}}
|
||||
co_return;
|
||||
}
|
||||
|
||||
resumable4 f6(float) {
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'promise_type'}}
|
||||
// expected-warning@-2 {{type aware 'operator delete' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable4_tad {{type aware 'operator delete' declared here}}
|
||||
// expected-note@#resumable4_tad {{member 'operator delete' declared here}}
|
||||
co_return;
|
||||
}
|
||||
|
||||
resumable5 f7(float) {
|
||||
// expected-warning@-1 {{type aware 'operator delete' will not be used for coroutine allocation}}
|
||||
// expected-note@#resumable5_tad {{type aware 'operator delete' declared here}}
|
||||
co_return;
|
||||
}
|
127
clang/test/SemaCXX/type-aware-new-constexpr.cpp
Normal file
127
clang/test/SemaCXX/type-aware-new-constexpr.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -fno-aligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -faligned-allocation -fexperimental-new-constant-interpreter
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -faligned-allocation -fexperimental-new-constant-interpreter
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -fno-aligned-allocation -fexperimental-new-constant-interpreter
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation -fexperimental-new-constant-interpreter
|
||||
|
||||
|
||||
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, std::align_val_t); // #1
|
||||
void operator delete(std::type_identity<S1>, void* ptr, size_t sz, std::align_val_t); // #2
|
||||
|
||||
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, std::align_val_t) = delete; // #3
|
||||
void operator delete(std::type_identity<S2>, void* ptr, size_t sz, std::align_val_t) = delete; // #4
|
||||
|
||||
constexpr int ensure_constexpr_retains_types_at_runtime() {
|
||||
// Verify we dont resolve typed allocators in const contexts
|
||||
S2 *s = new S2();
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#1 {{candidate function not viable: no known conversion from 'type_identity<S2>' to 'type_identity<S1>' for 1st argument}}
|
||||
// expected-note@#3 {{candidate function has been explicitly deleted}}
|
||||
auto result = s->i;
|
||||
delete s;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#4 {{'operator delete' has been explicitly marked deleted here}}
|
||||
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, std::align_val_t) = delete; // #5
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t sz, std::align_val_t) = delete; // #6
|
||||
};
|
||||
|
||||
template <typename T> void* operator new(std::type_identity<T>, size_t sz, std::align_val_t) = delete; // #7
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t sz, std::align_val_t) = delete; // #8
|
||||
|
||||
constexpr int constexpr_vs_inclass_operators() {
|
||||
S3 *s;
|
||||
if consteval {
|
||||
s = ::new S3();
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#1 {{candidate function not viable: no known conversion from 'type_identity<S3>' to 'type_identity<S1>' for 1st argument}}
|
||||
// expected-note@#3 {{candidate function not viable: no known conversion from 'type_identity<S3>' to 'type_identity<S2>' for 1st argument}}
|
||||
// expected-note@#7 {{candidate function [with T = S3] has been explicitly deleted}}
|
||||
} else {
|
||||
s = new S3();
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#5 {{candidate function [with T = S3] has been explicitly deleted}}
|
||||
}
|
||||
auto result = s->i;
|
||||
if consteval {
|
||||
::delete s;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#8 {{'operator delete<S3>' has been explicitly marked deleted here}}
|
||||
} else {
|
||||
delete s;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#6 {{'operator delete<S3>' has been explicitly marked deleted here}}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Test a variety of valid constant evaluation paths
|
||||
struct S4 {
|
||||
int i = 1;
|
||||
constexpr S4() __attribute__((noinline)) {}
|
||||
};
|
||||
|
||||
void* operator new(std::type_identity<S4>, size_t sz, std::align_val_t);
|
||||
void operator delete(std::type_identity<S4>, void *, size_t sz, std::align_val_t);
|
||||
|
||||
constexpr int do_dynamic_alloc(int n) {
|
||||
S4* s = new S4;
|
||||
int result = n * s->i;
|
||||
delete s;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <int N> struct Tag {
|
||||
};
|
||||
|
||||
static constexpr int force_do_dynamic_alloc = do_dynamic_alloc(5);
|
||||
|
||||
constexpr int test_consteval_calling_constexpr(int i) {
|
||||
if consteval {
|
||||
return do_dynamic_alloc(2 * i);
|
||||
}
|
||||
return do_dynamic_alloc(3 * i);
|
||||
}
|
||||
|
||||
int test_consteval(int n, Tag<test_consteval_calling_constexpr(2)>, Tag<do_dynamic_alloc(3)>) {
|
||||
static const int t1 = test_consteval_calling_constexpr(4);
|
||||
static const int t2 = do_dynamic_alloc(5);
|
||||
int t3 = test_consteval_calling_constexpr(6);
|
||||
int t4 = do_dynamic_alloc(7);
|
||||
return t1 * t2 * t3 * t4;
|
||||
}
|
58
clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
Normal file
58
clang/test/SemaCXX/type-aware-new-delete-arrays.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -faligned-allocation -Wall -Wpedantic
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -faligned-allocation -Wall -Wpedantic
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation -Wall -Wpedantic
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -fno-aligned-allocation -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, std::align_val_t) = delete; // #1
|
||||
template <typename T> void operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #2
|
||||
};
|
||||
void *operator new[](std::type_identity<BasicTypeAwareArrayAllocator>, size_t, std::align_val_t);
|
||||
void operator delete[](std::type_identity<BasicTypeAwareArrayAllocator>, void*, size_t, std::align_val_t);
|
||||
|
||||
struct BasicTypeAwareNonArrayAllocator {
|
||||
template <typename T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
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, std::align_val_t);
|
||||
template <typename T> void operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
void *operator new[](std::type_identity<WorkingTypeAwareAllocator>, size_t, std::align_val_t) = delete;
|
||||
void operator delete[](std::type_identity<WorkingTypeAwareAllocator>, void*, size_t, std::align_val_t) = delete;
|
||||
|
||||
|
||||
void test() {
|
||||
BasicTypeAwareArrayAllocator *A0 = new BasicTypeAwareArrayAllocator[10];
|
||||
// expected-error@-1 {{call to deleted function 'operator new[]'}}
|
||||
// expected-note@#1 {{candidate function [with T = BasicTypeAwareArrayAllocator] has been explicitly deleted}}
|
||||
delete [] A0;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#2 {{'operator delete[]<BasicTypeAwareArrayAllocator>' has been explicitly marked deleted here}}
|
||||
|
||||
BasicTypeAwareNonArrayAllocator *A1 = new BasicTypeAwareNonArrayAllocator[10];
|
||||
delete [] A1;
|
||||
|
||||
WorkingTypeAwareAllocator *A2 = new WorkingTypeAwareAllocator[10];
|
||||
// expected-note@-1 {{allocated with 'new[]' here}}
|
||||
delete A2;
|
||||
// expected-warning@-1 {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
|
||||
|
||||
WorkingTypeAwareAllocator *A3 = new WorkingTypeAwareAllocator;
|
||||
// expected-note@-1 {{allocated with 'new' here}}
|
||||
delete [] A3;
|
||||
// expected-warning@-1 {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fsized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fno-sized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fno-sized-deallocation -fno-aligned-allocation
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fsized-deallocation -fno-aligned-allocation
|
||||
|
||||
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, std::align_val_t); // #1
|
||||
void *operator new(std::type_identity<int>, size_t, std::align_val_t, TestType&); // #2
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #3
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, TestType&); // #4
|
||||
template <typename T> void *operator new(std::type_identity<TemplateTestType<T>>, size_t, std::align_val_t, TestType&); // #5
|
||||
template <typename T, typename U> void *operator new(std::type_identity<T>, size_t, std::align_val_t, TemplateTestType<U>&); // #6
|
||||
template <template <typename> class T> void *operator new(std::type_identity<T<int>>, size_t, std::align_val_t); // #7
|
||||
|
||||
void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #8
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #9
|
||||
template <typename T> void operator delete(std::type_identity<TemplateTestType<T>>, void *, size_t, std::align_val_t); // #10
|
||||
template <template <typename> class T> void operator delete(std::type_identity<T<int>>, void *, size_t, std::align_val_t); // #11
|
||||
|
||||
typedef std::type_identity<float> TypeIdentityAlias1;
|
||||
void *operator new(TypeIdentityAlias1, size_t, std::align_val_t); // #12
|
||||
|
||||
using TypeIdentityAlias2 = std::type_identity<double>;
|
||||
void *operator new(TypeIdentityAlias2, size_t, std::align_val_t); // #13
|
||||
|
||||
template <typename T> using TypeIdentityAlias3 = std::type_identity<T>;
|
||||
template <typename T> void *operator new(TypeIdentityAlias3<T>, size_t, std::align_val_t); // #14
|
||||
|
||||
template <typename T> void *operator new(T, size_t, std::align_val_t);
|
||||
// expected-error@-1 {{'operator new' cannot take a dependent type as its 1st parameter}}
|
||||
|
||||
template <typename T> void operator delete(T, void*, size_t, std::align_val_t);
|
||||
// expected-error@-1 {{'operator delete' cannot take a dependent type as its 1st parameter}}
|
||||
|
||||
template <typename T> struct S {
|
||||
typedef std::type_identity<T> type_identity;
|
||||
typedef size_t size_ty;
|
||||
typedef std::align_val_t align_val_ty;
|
||||
typedef void *ptr_ty;
|
||||
};
|
||||
|
||||
template <typename T> void *operator new(typename S<T>::type_identity, size_t, std::align_val_t);
|
||||
// expected-error@-1 {{'operator new' cannot take a dependent type as its 1st parameter}}
|
||||
|
||||
// Invalid type aware declarations
|
||||
void *operator new(std::type_identity<int>, size_t);
|
||||
// expected-error@-1 {{type aware 'operator new' must have at least three parameters}}
|
||||
void *operator new(std::type_identity<int>, size_t, TestType&);
|
||||
// expected-error@-1 {{type aware 'operator new' takes type std::align_val_t ('std::align_val_t') as 3rd parameter}}
|
||||
void operator delete(std::type_identity<int>, void *);
|
||||
// expected-error@-1 {{type aware 'operator delete' must have at least four parameters}}
|
||||
void operator delete(std::type_identity<int>, void *, size_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' must have at least four parameters}}
|
||||
void operator delete(std::type_identity<int>, void *, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' must have at least four parameters}}
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *);
|
||||
// expected-error@-1 {{type aware 'operator delete' must have at least four parameters}}
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' must have at least four parameters}}
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' must have at least four parameters}}
|
||||
template <typename T, typename U> void *operator new(std::type_identity<T>, U);
|
||||
// expected-error@-1 {{type aware 'operator new' must have at least three parameters}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, U, size_t, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter; use 'void *' instead}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, void *, U, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' cannot take a dependent type as its 3rd parameter; use 'unsigned long' instead}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, void *, size_t, U);
|
||||
// expected-error@-1 {{type aware 'operator delete' cannot take a dependent type as its 4th parameter; use 'std::align_val_t' instead}}
|
||||
template <typename U> void *operator new(std::type_identity<int>, typename S<U>::size_ty, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter; use size_t ('unsigned long') instead}}
|
||||
template <typename U> void operator delete(std::type_identity<int>, typename S<U>::ptr_ty, size_t, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter; use 'void *' instead}}
|
||||
template <typename T, typename U> void *operator new(std::type_identity<T>, typename S<U>::size_ty, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter; use size_t ('unsigned long') instead}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, typename S<U>::ptr_ty, size_t, std::align_val_t);
|
||||
// expected-error@-1 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter; use 'void *' instead}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, void *, size_t, typename S<U>::align_val_ty);
|
||||
// expected-error@-1 {{type aware 'operator delete' cannot take a dependent type as its 4th parameter; use 'std::align_val_t' instead}}
|
||||
|
||||
template <typename T> using Alias = T;
|
||||
template <typename T> using TypeIdentityAlias = std::type_identity<T>;
|
||||
typedef std::type_identity<double> TypedefAlias;
|
||||
using UsingAlias = std::type_identity<float>;
|
||||
void *operator new(Alias<size_t>, std::align_val_t);
|
||||
template <typename T> void *operator new(Alias<std::type_identity<T>>, Alias<size_t>, std::align_val_t);
|
||||
void *operator new(Alias<std::type_identity<int>>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(Alias<std::type_identity<T>>, void *, size_t, std::align_val_t);
|
||||
void operator delete(Alias<std::type_identity<int>>, void *, size_t, std::align_val_t);
|
||||
|
||||
template <typename T> void *operator new(TypeIdentityAlias<T>, size_t, std::align_val_t);
|
||||
void *operator new(TypeIdentityAlias<int>, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(TypeIdentityAlias<T>, void *, size_t, std::align_val_t);
|
||||
void operator delete(TypeIdentityAlias<int>, void *, size_t, std::align_val_t);
|
||||
|
||||
template <typename T> void *operator new(TypedefAlias, size_t, std::align_val_t);
|
||||
void *operator new(TypedefAlias, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
|
||||
void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
|
||||
|
||||
template <typename T> void *operator new(UsingAlias, size_t, std::align_val_t);
|
||||
void *operator new(UsingAlias, size_t, std::align_val_t);
|
||||
template <typename T> void operator delete(UsingAlias, void *, size_t, std::align_val_t);
|
||||
void operator delete(UsingAlias, void *, size_t, std::align_val_t);
|
||||
|
||||
class ForwardDecl;
|
||||
void *operator new(std::type_identity<ForwardDecl>, size_t, std::align_val_t);
|
||||
void operator delete(std::type_identity<ForwardDecl>, void*, size_t, std::align_val_t);
|
@ -0,0 +1,150 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify=expected,precxx26 %s -std=c++23
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26
|
||||
|
||||
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, std::align_val_t); // #1
|
||||
void operator delete(std::type_identity<S>, void *, size_t, std::align_val_t); // #2
|
||||
// precxx26-warning@#1 {{type aware allocators are a C++2c extension}}
|
||||
// precxx26-warning@#2 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(S *, std::destroying_delete_t);
|
||||
};
|
||||
|
||||
template <typename T> struct S2 {
|
||||
void *operator new(std::type_identity<S2<T>>, size_t, std::align_val_t); // #3
|
||||
void operator delete(std::type_identity<S2<T>>, void *, size_t, std::align_val_t); // #4
|
||||
// precxx26-warning@#3 {{type aware allocators are a C++2c extension}}
|
||||
// precxx26-warning@#4 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(S2 *, std::destroying_delete_t);
|
||||
};
|
||||
|
||||
struct S3 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #5
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #6
|
||||
// precxx26-warning@#5 {{type aware allocators are a C++2c extension}}
|
||||
// precxx26-warning@#6 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(S3 *, std::destroying_delete_t);
|
||||
};
|
||||
|
||||
struct S4 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #7
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t); // #8
|
||||
template <typename T> void operator delete(std::type_identity<T>, S4 *, std::destroying_delete_t, size_t, std::align_val_t); // #9
|
||||
// precxx26-warning@#7 {{type aware allocators are a C++2c extension}}
|
||||
// precxx26-warning@#8 {{type aware allocators are a C++2c extension}}
|
||||
// expected-error@#9 {{destroying delete is not permitted to be type aware}}
|
||||
};
|
||||
|
||||
struct S5 {
|
||||
template <typename T> void operator delete(std::type_identity<T>, T *, size_t, std::align_val_t); // #10
|
||||
// expected-error@#10 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter}}
|
||||
// precxx26-warning@#10 {{type aware allocators are a C++2c extension}}
|
||||
};
|
||||
|
||||
struct S6 {
|
||||
template <typename T> void *operator new(std::type_identity<S6>, T, std::align_val_t); // #11
|
||||
// expected-error@#11 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter}}
|
||||
// precxx26-warning@#11 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(std::type_identity<S6>, T, size_t, std::align_val_t); // #12
|
||||
// expected-error@#12 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter}}
|
||||
// precxx26-warning@#12 {{type aware allocators are a C++2c extension}}
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
struct S7 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, U, std::align_val_t); // #13
|
||||
// expected-error@#13 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter;}}
|
||||
// precxx26-warning@#13 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(std::type_identity<T>, U, size_t, std::align_val_t); // #14
|
||||
// expected-error@#14 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter;}}
|
||||
// precxx26-warning@#14 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(std::type_identity<T>, S7 *, std::destroying_delete_t, U, std::align_val_t); // #15
|
||||
// expected-error@#15 {{destroying delete is not permitted to be type aware}}
|
||||
void operator delete(S7 *, std::destroying_delete_t, U); // #16
|
||||
};
|
||||
|
||||
void f() {
|
||||
S7<int> s;
|
||||
// expected-note@-1 {{in instantiation of template class 'S7<int>' requested here}}
|
||||
// expected-error@#16 {{destroying operator delete can have only an optional size and optional alignment parameter}}
|
||||
}
|
||||
|
||||
struct S8 {
|
||||
template <typename T, typename U> void *operator new(std::type_identity<T>, U, std::align_val_t); // #17
|
||||
// expected-error@#17 {{type aware 'operator new' cannot take a dependent type as its 2nd parameter;}}
|
||||
// precxx26-warning@#17 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, U, size_t, std::align_val_t); // #18
|
||||
// expected-error@#18 {{type aware 'operator delete' cannot take a dependent type as its 2nd parameter;}}
|
||||
// precxx26-warning@#18 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T, typename U> void operator delete(std::type_identity<T>, S8 *, std::destroying_delete_t, U, std::align_val_t); // #19
|
||||
// expected-error@#19 {{destroying delete is not permitted to be type aware}}
|
||||
};
|
||||
|
||||
template <typename T> using Alias = T;
|
||||
template <typename T> using TypeIdentityAlias = std::type_identity<T>;
|
||||
typedef std::type_identity<double> TypedefAlias;
|
||||
using UsingAlias = std::type_identity<float>;
|
||||
struct S9 {
|
||||
void *operator new(Alias<size_t>, std::align_val_t);
|
||||
template <typename T> void *operator new(Alias<std::type_identity<T>>, Alias<size_t>, std::align_val_t); // #20
|
||||
// precxx26-warning@#20 {{type aware allocators are a C++2c extension}}
|
||||
void *operator new(Alias<std::type_identity<int>>, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(Alias<std::type_identity<T>>, void *, size_t, std::align_val_t); // #21
|
||||
// precxx26-warning@#21{{type aware allocators are a C++2c extension}}
|
||||
void operator delete(Alias<std::type_identity<int>>, void *, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
};
|
||||
struct S10 {
|
||||
template <typename T> void *operator new(TypeIdentityAlias<T>, size_t, std::align_val_t); // #22
|
||||
// precxx26-warning@#22 {{type aware allocators are a C++2c extension}}
|
||||
void *operator new(TypeIdentityAlias<int>, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(TypeIdentityAlias<T>, void *, size_t, std::align_val_t); // #23
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(TypeIdentityAlias<int>, void *, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
};
|
||||
|
||||
void test() {
|
||||
S9 *s9 = new S9;
|
||||
delete s9;
|
||||
S10 *s10 = new S10;
|
||||
delete s10;
|
||||
}
|
||||
|
||||
struct S11 {
|
||||
template <typename T> void *operator new(TypedefAlias, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
void *operator new(TypedefAlias, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(TypedefAlias, void *, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
};
|
||||
struct S12 {
|
||||
template <typename T> void *operator new(UsingAlias, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
void *operator new(UsingAlias, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
template <typename T> void operator delete(UsingAlias, void *, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(UsingAlias, void *, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
};
|
||||
|
||||
struct S13 {
|
||||
void *operator new(std::type_identity<S13>, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
void operator delete(std::type_identity<S13>, void*, size_t, std::align_val_t);
|
||||
// precxx26-warning@-1 {{type aware allocators are a C++2c extension}}
|
||||
};
|
472
clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
Normal file
472
clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
Normal file
@ -0,0 +1,472 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fsized-deallocation -fno-aligned-allocation
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -fexceptions -fno-sized-deallocation -fno-aligned-allocation
|
||||
|
||||
namespace std {
|
||||
template <class T> struct type_identity {};
|
||||
enum class align_val_t : __SIZE_TYPE__ {};
|
||||
struct destroying_delete_t { explicit destroying_delete_t() = default; };
|
||||
}
|
||||
|
||||
#if defined(__cpp_aligned_new)
|
||||
#define ALLOCATION_ALIGNMENT , std::align_val_t
|
||||
#else
|
||||
#define ALLOCATION_ALIGNMENT
|
||||
#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; // #1
|
||||
void operator delete(void *) = delete; //#2
|
||||
};
|
||||
void *operator new(std::type_identity<UntypedInclassNew>, size_t, std::align_val_t); // #3
|
||||
void operator delete(std::type_identity<UntypedInclassNew>, void*, size_t, std::align_val_t); // #4
|
||||
|
||||
|
||||
struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_NoAlignedAlloc {
|
||||
void *operator new(size_t) = delete; // #5
|
||||
void operator delete(void *) = delete; // #6
|
||||
};
|
||||
void *operator new(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, size_t, std::align_val_t); // #7
|
||||
void operator delete(std::type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>, void *, size_t, std::align_val_t); // #8
|
||||
|
||||
struct __attribute__((aligned(128))) UntypedInclassNewOveraligned_AlignedAlloc {
|
||||
void *operator new(size_t ALLOCATION_ALIGNMENT) = delete; // #9
|
||||
void operator delete(void * ALLOCATION_ALIGNMENT) = delete; // #10
|
||||
};
|
||||
void *operator new(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, size_t, std::align_val_t); // #11
|
||||
void operator delete(std::type_identity<UntypedInclassNewOveraligned_AlignedAlloc>, void *, size_t, std::align_val_t); // #12
|
||||
|
||||
struct BasicClass {};
|
||||
void *operator new(std::type_identity<BasicClass>, size_t, std::align_val_t) = delete; // #13
|
||||
void operator delete(std::type_identity<BasicClass>, void *, size_t, std::align_val_t) = delete; // #14
|
||||
|
||||
struct InclassNew1 {
|
||||
void *operator new(std::type_identity<InclassNew1>, size_t, std::align_val_t) = delete; // #15
|
||||
void operator delete(std::type_identity<InclassNew1>, void *, size_t, std::align_val_t) = delete; // #16
|
||||
};
|
||||
void *operator new(std::type_identity<InclassNew1>, size_t, std::align_val_t); // #17
|
||||
void operator delete(std::type_identity<InclassNew1>, void *, size_t, std::align_val_t); // #18
|
||||
|
||||
struct InclassNew2 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #19
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t) = delete; // #20
|
||||
};
|
||||
void *operator new(std::type_identity<InclassNew2>, size_t, std::align_val_t); // #21
|
||||
void operator delete(std::type_identity<InclassNew2>, void *, size_t, std::align_val_t); // #22
|
||||
|
||||
struct InclassNew3 {
|
||||
void *operator new(std::type_identity<InclassNew3>, size_t, std::align_val_t) = delete; // #23
|
||||
void operator delete(std::type_identity<InclassNew3>, void*, size_t, std::align_val_t) = delete; // #24
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #25
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #26
|
||||
};
|
||||
|
||||
struct __attribute__((aligned(128))) InclassNew4 {
|
||||
void *operator new(std::type_identity<InclassNew4>, size_t, std::align_val_t); // #27
|
||||
void operator delete(std::type_identity<InclassNew4>, void*, size_t, std::align_val_t); // #28
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #29
|
||||
template <typename T> void operator delete(std::type_identity<T>, void *, size_t, std::align_val_t) = delete; // #30
|
||||
};
|
||||
|
||||
struct InclassNew5 {
|
||||
InclassNew5();
|
||||
void *operator new(std::type_identity<InclassNew5>, size_t, std::align_val_t); // #31
|
||||
void operator delete(void *); // #32
|
||||
void operator delete(std::type_identity<InclassNew5>, void*, size_t, std::align_val_t) = delete; // #33
|
||||
};
|
||||
|
||||
struct InclassNew6 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in 'InclassNew6' must have matching type aware 'operator new'}}
|
||||
// expected-note@#36 {{unmatched type aware 'operator delete' declared here}}
|
||||
InclassNew6();
|
||||
void *operator new(size_t); // #34
|
||||
void operator delete(void *) = delete; // #35
|
||||
void operator delete(std::type_identity<InclassNew6>, void*, size_t, std::align_val_t) = delete; // #36
|
||||
};
|
||||
|
||||
struct InclassNew7 {
|
||||
InclassNew7();
|
||||
void *operator new(std::type_identity<InclassNew7>, size_t, std::align_val_t); // #37
|
||||
void operator delete(std::type_identity<InclassNew7>, void*, size_t, std::align_val_t); // #38
|
||||
void operator delete(InclassNew7 *, std::destroying_delete_t) = delete; // #39
|
||||
};
|
||||
|
||||
struct InclassNew8 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in 'InclassNew8' must have matching type aware 'operator delete'}}
|
||||
// expected-note@#40 {{unmatched type aware 'operator new' declared here}}
|
||||
InclassNew8();
|
||||
void *operator new(std::type_identity<InclassNew8>, size_t, std::align_val_t); // #40
|
||||
void operator delete(void*); // #41
|
||||
};
|
||||
|
||||
struct InclassNew9 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in 'InclassNew9' must have matching type aware 'operator delete'}}
|
||||
// expected-note@#42 {{unmatched type aware 'operator new' declared here}}
|
||||
InclassNew9();
|
||||
void *operator new(std::type_identity<InclassNew9>, size_t, std::align_val_t); // #42
|
||||
};
|
||||
|
||||
void operator delete(std::type_identity<InclassNew9>, void*, size_t, std::align_val_t); // #43
|
||||
|
||||
struct BaseClass1 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #44
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #45
|
||||
virtual ~BaseClass1();
|
||||
};
|
||||
BaseClass1::~BaseClass1() {
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#45 {{'operator delete<BaseClass1>' has been explicitly marked deleted here}}
|
||||
}
|
||||
|
||||
struct SubClass1 : BaseClass1 {
|
||||
virtual ~SubClass1();
|
||||
};
|
||||
|
||||
SubClass1::~SubClass1() {
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#45 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
|
||||
}
|
||||
|
||||
struct BaseClass2 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #46
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #47
|
||||
void operator delete(BaseClass2 *, std::destroying_delete_t); // #48
|
||||
virtual ~BaseClass2();
|
||||
};
|
||||
BaseClass2::~BaseClass2(){
|
||||
};
|
||||
|
||||
struct SubClass2 : BaseClass2 {
|
||||
SubClass2(); // Force exception cleanup which should invoke type aware delete
|
||||
virtual ~SubClass2();
|
||||
};
|
||||
SubClass2::~SubClass2(){
|
||||
}
|
||||
|
||||
struct BaseClass3 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #49
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #50
|
||||
void operator delete(BaseClass3 *, std::destroying_delete_t) = delete; // #51
|
||||
virtual ~BaseClass3();
|
||||
};
|
||||
BaseClass3::~BaseClass3(){
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#51 {{'operator delete' has been explicitly marked deleted here}}
|
||||
}
|
||||
|
||||
struct SubClass3 : BaseClass3 {
|
||||
virtual ~SubClass3();
|
||||
};
|
||||
SubClass3::~SubClass3(){
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#51 {{'operator delete' has been explicitly marked deleted here}}
|
||||
}
|
||||
|
||||
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, std::align_val_t) = delete; // #52
|
||||
template <Derived<SubClass4> T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #53
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #54
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #55
|
||||
|
||||
virtual ~BaseClass4();
|
||||
};
|
||||
BaseClass4::~BaseClass4() {
|
||||
}
|
||||
|
||||
struct SubClass4 : BaseClass4 {
|
||||
virtual ~SubClass4();
|
||||
};
|
||||
SubClass4::~SubClass4(){
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#53 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
|
||||
}
|
||||
|
||||
struct SubClass4_1 : SubClass4 {
|
||||
SubClass4_1();
|
||||
};
|
||||
struct SubClass4_2 : BaseClass4 {
|
||||
};
|
||||
|
||||
struct SubClass5;
|
||||
struct BaseClass5 {
|
||||
template <Same<SubClass5> T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #56
|
||||
template <Same<SubClass5> T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #57
|
||||
template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void *operator new(std::type_identity<T>, size_t, std::align_val_t) = delete; // #58
|
||||
template <Derived<SubClass5> T> requires (!Same<SubClass5, T>) void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #59
|
||||
};
|
||||
|
||||
struct SubClass5 : BaseClass5 {
|
||||
};
|
||||
struct SubClass5_1 : SubClass5 {
|
||||
};
|
||||
|
||||
|
||||
struct BaseClass6 {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #60
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #61
|
||||
BaseClass6();
|
||||
virtual ~BaseClass6();
|
||||
};
|
||||
BaseClass6::~BaseClass6(){
|
||||
}
|
||||
|
||||
struct SubClass6_1 : BaseClass6 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator new' in 'SubClass6_1' must have matching type aware 'operator delete'}}
|
||||
// expected-note@#62 {{unmatched type aware 'operator new' declared here}}
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #62
|
||||
SubClass6_1();
|
||||
};
|
||||
struct SubClass6_2 : BaseClass6 {
|
||||
// expected-error@-1 {{declaration of type aware 'operator delete' in 'SubClass6_2' must have matching type aware 'operator new'}}
|
||||
// expected-note@#63 {{unmatched type aware 'operator delete' declared here}}
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #63
|
||||
SubClass6_2();
|
||||
};
|
||||
|
||||
struct MultiDimensionArrayTest1 {
|
||||
int i;
|
||||
MultiDimensionArrayTest1();
|
||||
template <typename T, unsigned N> void *operator new[](std::type_identity<T[N]>, size_t, std::align_val_t) = delete; // #64
|
||||
template <typename T, unsigned N> void operator delete[](std::type_identity<T[N]>, void*, size_t, std::align_val_t) = delete; // #65
|
||||
};
|
||||
|
||||
struct MultiDimensionArrayTest2 {
|
||||
int i;
|
||||
MultiDimensionArrayTest2();
|
||||
template <unsigned N> void *operator new[](std::type_identity<MultiDimensionArrayTest2[N]>, size_t, std::align_val_t) = delete; // #66
|
||||
template <unsigned N> void operator delete[](std::type_identity<MultiDimensionArrayTest2[N]>, void*, size_t, std::align_val_t) = delete; // #67
|
||||
};
|
||||
|
||||
struct MultiDimensionArrayTest3 {
|
||||
int i;
|
||||
MultiDimensionArrayTest3();
|
||||
template <unsigned N> requires (N%4 == 0) void *operator new[](std::type_identity<MultiDimensionArrayTest3[N]>, size_t, std::align_val_t) = delete; // #68
|
||||
template <unsigned N> requires (N%4 == 0) void operator delete[](std::type_identity<MultiDimensionArrayTest3[N]>, void*, size_t, std::align_val_t) = delete; // #69
|
||||
};
|
||||
|
||||
struct ClassScopedTemplatePackStruct {
|
||||
template <class T, class... Pack> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Pack...);
|
||||
template <class T, class... Pack> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Pack...); // #70
|
||||
};
|
||||
|
||||
void test() {
|
||||
|
||||
// untyped in class declaration wins
|
||||
UntypedInclassNew *O1 = new UntypedInclassNew;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#1 {{candidate function has been explicitly deleted}}
|
||||
delete O1;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#2 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// untyped in class declaration wins, even though global is aligned and in class is not
|
||||
UntypedInclassNewOveraligned_NoAlignedAlloc *O2 = new UntypedInclassNewOveraligned_NoAlignedAlloc;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#5 {{candidate function has been explicitly deleted}}
|
||||
delete O2;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#6 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// untyped in class declaration wins
|
||||
UntypedInclassNewOveraligned_AlignedAlloc *O3 = new UntypedInclassNewOveraligned_AlignedAlloc;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#9 {{candidate function has been explicitly deleted}}
|
||||
delete O3;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#10 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// We resolve the explicitly typed free operator
|
||||
BasicClass *O4 = new BasicClass;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#13 {{candidate function has been explicitly deleted}}
|
||||
// expected-note@#3 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<UntypedInclassNew>' for 1st argument}}
|
||||
// expected-note@#17 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<InclassNew1>' for 1st argument}}
|
||||
// expected-note@#21 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<InclassNew2>' for 1st argument}}
|
||||
// expected-note@#7 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<UntypedInclassNewOveraligned_NoAlignedAlloc>' for 1st argument}}
|
||||
// expected-note@#11 {{candidate function not viable: no known conversion from 'type_identity<BasicClass>' to 'type_identity<UntypedInclassNewOveraligned_AlignedAlloc>' for 1st argument}}
|
||||
|
||||
delete O4;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#14 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// We resolve the explicitly typed in class operator
|
||||
InclassNew1 *O5 = new InclassNew1;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#15 {{candidate function has been explicitly deleted}}
|
||||
delete O5;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#16 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// We resolve the unconstrained in class operators over the constrained free operators
|
||||
InclassNew2 *O6 = new InclassNew2;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#19 {{candidate function [with T = InclassNew2] has been explicitly deleted}}
|
||||
delete O6;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#20 {{'operator delete<InclassNew2>' has been explicitly marked deleted here}}
|
||||
|
||||
// We prefer the constrained in class operators over the unconstrained variants
|
||||
InclassNew3 *O7 = new InclassNew3;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#23 {{candidate function has been explicitly deleted}}
|
||||
// expected-note@#25 {{candidate function [with T = InclassNew3]}}
|
||||
delete O7;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#24 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// Constructor clean up invokes typed operator if typed new was used
|
||||
InclassNew5 *O9 = new InclassNew5;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#33 {{'operator delete' has been explicitly marked deleted here}}
|
||||
delete O9;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#33 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// Constructor clean up invokes untyped delete if untyped delete was used
|
||||
InclassNew6 *O10 = new InclassNew6;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#36 {{'operator delete' has been explicitly marked deleted here}}
|
||||
// expected-error@-3 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
|
||||
// expected-note@#34 {{non-type aware 'operator new' declared here in 'InclassNew6'}}
|
||||
// expected-note@#36 {{type aware 'operator delete' declared here}}
|
||||
delete O10;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#36 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
// Destroying delete is prefered over typed delete
|
||||
InclassNew7 *O11 = new InclassNew7;
|
||||
delete O11;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#39 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
InclassNew8 *O12 = new InclassNew8;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#40 {{type aware 'operator new' declared here in 'InclassNew8'}}
|
||||
// expected-note@#41 {{non-type aware 'operator delete' declared here}}
|
||||
delete O12;
|
||||
|
||||
InclassNew9 *O13 = new InclassNew9;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#42 {{type aware 'operator new' declared here in 'InclassNew9'}}
|
||||
|
||||
delete O13;
|
||||
|
||||
// Creating the virtual destructor for an type requires the deleting destructor
|
||||
// for that type
|
||||
SubClass1 *O14 = new SubClass1;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#45 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
|
||||
|
||||
delete O14;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#45 {{'operator delete<SubClass1>' has been explicitly marked deleted here}}
|
||||
|
||||
SubClass2 *O15 = new SubClass2;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#47 {{'operator delete<SubClass2>' has been explicitly marked deleted here}}
|
||||
delete O15;
|
||||
|
||||
// Deletion triggers destroying delete despite type aware delete
|
||||
SubClass3 *O16 = new SubClass3;
|
||||
delete O16;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#51 {{'operator delete' has been explicitly marked deleted here}}
|
||||
|
||||
SubClass4 *O17 = new SubClass4;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#52 {{candidate function [with T = SubClass4] has been explicitly deleted}}
|
||||
// expected-note@#54 {{candidate function [with T = SubClass4]}}
|
||||
delete O17;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#53 {{'operator delete<SubClass4>' has been explicitly marked deleted here}}
|
||||
|
||||
SubClass4_1 *O18 = new SubClass4_1;
|
||||
delete O18;
|
||||
|
||||
SubClass4_2 *O19 = new SubClass4_2;
|
||||
delete O19;
|
||||
|
||||
SubClass5 *O20 = new SubClass5;
|
||||
delete O20;
|
||||
|
||||
SubClass5_1 *O21 = new SubClass5_1;
|
||||
// expected-error@-1 {{no matching function for call to 'operator new'}}
|
||||
delete O21;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'SubClass5_1'}}
|
||||
// expected-note@#57 {{member 'operator delete' declared here}}
|
||||
// expected-note@#59 {{member 'operator delete' declared here}}
|
||||
|
||||
SubClass6_1 *O22 = new SubClass6_1;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#62 {{type aware 'operator new' declared here in 'SubClass6_1'}}
|
||||
// expected-note@#61 {{type aware 'operator delete' declared here in 'BaseClass6'}}
|
||||
delete O22;
|
||||
|
||||
SubClass6_2 *O23 = new SubClass6_2;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#60 {{type aware 'operator new' declared here in 'BaseClass6'}}
|
||||
// expected-note@#63 {{type aware 'operator delete' declared here in 'SubClass6_2'}}
|
||||
delete O23;
|
||||
|
||||
MultiDimensionArrayTest1 *O24 = new MultiDimensionArrayTest1;
|
||||
delete O24;
|
||||
|
||||
MultiDimensionArrayTest1 *O25 = new MultiDimensionArrayTest1[10];
|
||||
// expected-error@-1 {{no matching function for call to 'operator new[]'}}
|
||||
delete [] O25;
|
||||
// expected-error@-1 {{no suitable member 'operator delete[]' in 'MultiDimensionArrayTest1'}}
|
||||
// expected-note@#65 {{member 'operator delete[]' declared here}}
|
||||
|
||||
{
|
||||
using InnerArray = MultiDimensionArrayTest1[3];
|
||||
InnerArray *O26 = new InnerArray[7];
|
||||
// expected-error@-1 {{call to deleted function 'operator new[]'}}
|
||||
// expected-note@#64 {{candidate function [with T = MultiDimensionArrayTest1, N = 3] has been explicitly deleted}}
|
||||
delete [] O26;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#65 {{'operator delete[]<MultiDimensionArrayTest1, 3U>' has been explicitly marked deleted here}}
|
||||
}
|
||||
{
|
||||
using InnerArray = MultiDimensionArrayTest2[3];
|
||||
InnerArray *O27 = new InnerArray[7];
|
||||
// expected-error@-1 {{call to deleted function 'operator new[]'}}
|
||||
// expected-note@#66 {{candidate function [with N = 3] has been explicitly deleted}}
|
||||
delete [] O27;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#67 {{'operator delete[]<3U>' has been explicitly marked deleted here}}
|
||||
}
|
||||
{
|
||||
using InnerArray = MultiDimensionArrayTest3[3];
|
||||
InnerArray *O28 = new InnerArray[3];
|
||||
// expected-error@-1 {{no matching function for call to 'operator new[]'}}
|
||||
delete [] O28;
|
||||
// expected-error@-1 {{no suitable member 'operator delete[]' in 'MultiDimensionArrayTest3'}}
|
||||
// expected-note@#69 {{member 'operator delete[]' declared here}}
|
||||
}
|
||||
{
|
||||
using InnerArray = MultiDimensionArrayTest3[4];
|
||||
InnerArray *O29 = new InnerArray[3];
|
||||
// expected-error@-1 {{call to deleted function 'operator new[]'}}
|
||||
// expected-note@#68 {{candidate function [with N = 4] has been explicitly deleted}}
|
||||
delete [] O29;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#69 {{'operator delete[]<4U>' has been explicitly marked deleted here}}
|
||||
}
|
||||
{
|
||||
ClassScopedTemplatePackStruct *O30 = new ClassScopedTemplatePackStruct;
|
||||
delete O30;
|
||||
// expected-error@-1 {{no suitable member 'operator delete' in 'ClassScopedTemplatePackStruct'}}
|
||||
// expected-note@#70 {{member 'operator delete' declared here}}
|
||||
}
|
||||
}
|
114
clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
Normal file
114
clang/test/SemaCXX/type-aware-new-delete-qualifiers.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26 -fsized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26 -fno-sized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26 -fno-sized-deallocation -fno-aligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -DNO_TADD -std=c++26 -fsized-deallocation -fno-aligned-allocation
|
||||
namespace std {
|
||||
template <class T> struct type_identity {
|
||||
typedef T type;
|
||||
};
|
||||
enum class align_val_t : __SIZE_TYPE__ {};
|
||||
struct destroying_delete_t { explicit destroying_delete_t() = default; };
|
||||
}
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
|
||||
|
||||
template <class Tp> struct is_const {
|
||||
static const bool value = false;
|
||||
};
|
||||
template <class Tp> struct is_const<Tp const> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <class Tp> struct is_volatile {
|
||||
static const bool value = false;
|
||||
};
|
||||
template <class Tp> struct is_volatile<Tp volatile> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <class T> static const bool is_const_v = is_const<T>::value;
|
||||
template <class T> static const bool is_volatile_v = is_volatile<T>::value;
|
||||
|
||||
struct VerifyQualifiers {
|
||||
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t) throw() {
|
||||
static_assert(is_const_v<T> == false); // #1
|
||||
static_assert(is_volatile_v<T> == false); // #2
|
||||
return 0;
|
||||
}
|
||||
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) {
|
||||
static_assert(is_const_v<T> == false); // #3
|
||||
static_assert(is_volatile_v<T> == false); // #4
|
||||
}
|
||||
template <typename T> void *operator new(std::type_identity<_Atomic T>, size_t, std::align_val_t) throw() {
|
||||
static_assert(is_const_v<T> == false);
|
||||
static_assert(is_volatile_v<T> == false);
|
||||
}
|
||||
};
|
||||
|
||||
void *operator new(std::type_identity<VerifyQualifiers> type, size_t, std::align_val_t) throw() { // #11
|
||||
static_assert(is_const_v<typename decltype(type)::type> == false); // #5
|
||||
static_assert(is_volatile_v<typename decltype(type)::type> == false); // #6
|
||||
return 0;
|
||||
}
|
||||
|
||||
void operator delete(std::type_identity<VerifyQualifiers> type, void*, size_t, std::align_val_t) {
|
||||
static_assert(is_const_v<typename decltype(type)::type> == false); // #7
|
||||
static_assert(is_volatile_v<typename decltype(type)::type> == false); // #8
|
||||
}
|
||||
|
||||
void *operator new(std::type_identity<int>, size_t, std::align_val_t) throw() = delete; // #12
|
||||
void operator delete(std::type_identity<int>, void*, size_t, std::align_val_t) = delete;
|
||||
|
||||
struct TestAtomic1 {
|
||||
|
||||
};
|
||||
struct TestAtomic2 {
|
||||
};
|
||||
|
||||
void *operator new(std::type_identity<TestAtomic1>, size_t, std::align_val_t) throw() = delete; // #13
|
||||
void operator delete(std::type_identity<_Atomic TestAtomic1>, void*, size_t, std::align_val_t) = delete; // #9
|
||||
void *operator new(std::type_identity<_Atomic TestAtomic2>, size_t, std::align_val_t) = delete; // #10
|
||||
void operator delete(std::type_identity<TestAtomic2>, void*, size_t, std::align_val_t) = delete;
|
||||
|
||||
// Success tests
|
||||
void test_member_allocators() {
|
||||
auto *unqualified_obj = new VerifyQualifiers();
|
||||
delete unqualified_obj;
|
||||
auto *const_obj = new const VerifyQualifiers();
|
||||
delete const_obj;
|
||||
auto *volatile_obj = new volatile VerifyQualifiers();
|
||||
delete volatile_obj;
|
||||
auto *const_volatile_obj = new const volatile VerifyQualifiers();
|
||||
delete const_volatile_obj;
|
||||
auto *atomic_obj = new _Atomic VerifyQualifiers();
|
||||
delete atomic_obj;
|
||||
auto *atomic_test1 = new _Atomic TestAtomic1;
|
||||
delete atomic_test1;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#9 {{'operator delete' has been explicitly marked deleted here}}
|
||||
auto *atomic_test2 = new _Atomic TestAtomic2;
|
||||
// expected-error@-1 {{call to deleted function 'operator new'}}
|
||||
// expected-note@#10 {{candidate function has been explicitly deleted}}
|
||||
// expected-note@#11 {{candidate function not viable}}
|
||||
// expected-note@#12 {{candidate function not viable}}
|
||||
// expected-note@#13 {{candidate function not viable}}
|
||||
delete atomic_test2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void test_global_allocators() {
|
||||
auto *unqualified_obj = ::new VerifyQualifiers();
|
||||
::delete unqualified_obj;
|
||||
auto *const_obj = ::new const VerifyQualifiers();
|
||||
::delete const_obj;
|
||||
auto *volatile_obj = ::new volatile VerifyQualifiers();
|
||||
::delete volatile_obj;
|
||||
auto *const_volatile_obj = ::new const volatile VerifyQualifiers();
|
||||
::delete const_volatile_obj;
|
||||
_Atomic VerifyQualifiers *atomic_obj = ::new _Atomic VerifyQualifiers();
|
||||
::delete atomic_obj;
|
||||
_Atomic int *atomic_int = new _Atomic int;
|
||||
delete atomic_int;
|
||||
}
|
59
clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
Normal file
59
clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=0
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=1
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=2
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=3
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=4
|
||||
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s -std=c++26
|
||||
|
||||
namespace std {
|
||||
#if !defined(INVALID_TYPE_IDENTITY_VERSION)
|
||||
// expected-no-diagnostics
|
||||
template <class T> struct type_identity {
|
||||
};
|
||||
#define TYPE_IDENTITY(T) std::type_identity<T>
|
||||
#elif INVALID_TYPE_IDENTITY_VERSION==0
|
||||
struct type_identity {};
|
||||
// expected-error@-1 {{std::type_identity must be a class template with a single type parameter}}
|
||||
#define TYPE_IDENTITY(T) std::type_identity
|
||||
#elif INVALID_TYPE_IDENTITY_VERSION==1
|
||||
template <class A, class B> struct type_identity {};
|
||||
// expected-error@-1 {{std::type_identity must be a class template with a single type parameter}}
|
||||
#define TYPE_IDENTITY(T) std::type_identity<T, int>
|
||||
#elif INVALID_TYPE_IDENTITY_VERSION==2
|
||||
enum type_identity {};
|
||||
// expected-error@-1 {{std::type_identity must be a class template with a single type parameter}}
|
||||
#define TYPE_IDENTITY(T) std::type_identity
|
||||
#elif INVALID_TYPE_IDENTITY_VERSION==3
|
||||
template <class T> using type_identity = int;
|
||||
#define TYPE_IDENTITY(T) std::type_identity<T>
|
||||
#elif INVALID_TYPE_IDENTITY_VERSION==4
|
||||
template <class T> struct inner {};
|
||||
template <class T> using type_identity = inner<T>;
|
||||
#define TYPE_IDENTITY(T) std::type_identity<T>
|
||||
#endif
|
||||
using size_t = __SIZE_TYPE__;
|
||||
enum class align_val_t : long {};
|
||||
}
|
||||
|
||||
template <class T> void *operator new(TYPE_IDENTITY(T), std::size_t, std::align_val_t); // #operator_new
|
||||
template <class T> void operator delete(TYPE_IDENTITY(T), void*, std::size_t, std::align_val_t); // #operator_delete
|
||||
|
||||
// These error messages aren't great, but they fall out of the way we model
|
||||
// alias types. Getting them in this way requires extremely unlikely code to be
|
||||
// used, so this is not terrible.
|
||||
|
||||
#if INVALID_TYPE_IDENTITY_VERSION==3
|
||||
// expected-error@#operator_new {{'operator new' takes type size_t ('unsigned long') as 1st parameter}}
|
||||
// expected-error@#operator_delete {{1st parameter of 'operator delete' must have type 'void *'}}
|
||||
#elif INVALID_TYPE_IDENTITY_VERSION==4
|
||||
// expected-error@#operator_new {{'operator new' cannot take a dependent type as its 1st parameter; use size_t ('unsigned long') instead}}
|
||||
// expected-error@#operator_delete {{'operator delete' cannot take a dependent type as its 1st parameter; use 'void *' instead}}
|
||||
#endif
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
struct TestType {};
|
||||
|
||||
void f() {
|
||||
TestType *t = new TestType;
|
||||
delete t;
|
||||
}
|
73
clang/test/SemaCXX/type-aware-placement-operators.cpp
Normal file
73
clang/test/SemaCXX/type-aware-placement-operators.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fsized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation -faligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fno-sized-deallocation -fno-aligned-allocation
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -fcxx-exceptions -fsized-deallocation -fno-aligned-allocation
|
||||
|
||||
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 Context;
|
||||
struct S1 {
|
||||
S1();
|
||||
};
|
||||
void *operator new(std::type_identity<S1>, size_t, std::align_val_t, Context&);
|
||||
void operator delete(std::type_identity<S1>, void*, size_t, std::align_val_t, Context&) = delete; // #1
|
||||
|
||||
struct S2 {
|
||||
S2();
|
||||
template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&);
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Context&) = delete; // #2
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #3
|
||||
};
|
||||
|
||||
struct S3 {
|
||||
S3();
|
||||
template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t) = delete; // #4
|
||||
};
|
||||
|
||||
struct S4 {
|
||||
S4();
|
||||
template<typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Context&); // #S4_new
|
||||
template<typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #5
|
||||
};
|
||||
|
||||
void test(Context& Ctx) {
|
||||
S1 *s1 = new (Ctx) S1;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#1 {{'operator delete' has been explicitly marked deleted here}}
|
||||
delete s1;
|
||||
S2 *s2_1 = new (Ctx) S2;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#2 {{'operator delete<S2>' has been explicitly marked deleted here}}
|
||||
// expected-note@#3 {{'operator delete<S2>' has been explicitly marked deleted here}}
|
||||
delete s2_1;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#2 {{'operator delete<S2>' has been explicitly marked deleted here}}
|
||||
S2 *s2_2 = new (std::align_val_t(128), Ctx) S2;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
delete s2_2;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#3 {{'operator delete<S2>' has been explicitly marked deleted here}}
|
||||
S3 *s3_1 = new S3;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
|
||||
delete s3_1;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
|
||||
S3 *s3_2 = new (std::align_val_t(128)) S3;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
|
||||
delete s3_2;
|
||||
// expected-error@-1 {{attempt to use a deleted function}}
|
||||
// expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
|
||||
|
||||
S4 *s4_1 = new (Ctx) S4;
|
||||
// expected-error@-1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
|
||||
// expected-note@#S4_new {{type aware 'operator new' declared here in 'S4'}}
|
||||
delete s4_1;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user