forked from OSchip/llvm-project
P0035R4: Semantic analysis and code generation for C++17 overaligned
allocation. llvm-svn: 283722
This commit is contained in:
parent
ed84f4abd4
commit
189e52fcdf
|
@ -1881,6 +1881,11 @@ public:
|
|||
unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; }
|
||||
unsigned getTypeAlign(const Type *T) const { return getTypeInfo(T).Align; }
|
||||
|
||||
/// \brief Return the ABI-specified alignment of a type, in bits, or 0 if
|
||||
/// the type is incomplete and we cannot determine the alignment (for
|
||||
/// example, from alignment attributes).
|
||||
unsigned getTypeAlignIfKnown(QualType T) const;
|
||||
|
||||
/// \brief Return the ABI-specified alignment of a (complete) type \p T, in
|
||||
/// characters.
|
||||
CharUnits getTypeAlignInChars(QualType T) const;
|
||||
|
|
|
@ -1838,11 +1838,13 @@ class CXXNewExpr : public Expr {
|
|||
unsigned GlobalNew : 1;
|
||||
/// Do we allocate an array? If so, the first SubExpr is the size expression.
|
||||
unsigned Array : 1;
|
||||
/// Should the alignment be passed to the allocation function?
|
||||
unsigned PassAlignment : 1;
|
||||
/// If this is an array allocation, does the usual deallocation
|
||||
/// function for the allocated type want to know the allocated size?
|
||||
unsigned UsualArrayDeleteWantsSize : 1;
|
||||
/// The number of placement new arguments.
|
||||
unsigned NumPlacementArgs : 13;
|
||||
unsigned NumPlacementArgs : 26;
|
||||
/// What kind of initializer do we have? Could be none, parens, or braces.
|
||||
/// In storage, we distinguish between "none, and no initializer expr", and
|
||||
/// "none, but an implicit initializer expr".
|
||||
|
@ -1858,8 +1860,8 @@ public:
|
|||
};
|
||||
|
||||
CXXNewExpr(const ASTContext &C, bool globalNew, FunctionDecl *operatorNew,
|
||||
FunctionDecl *operatorDelete, bool usualArrayDeleteWantsSize,
|
||||
ArrayRef<Expr*> placementArgs,
|
||||
FunctionDecl *operatorDelete, bool PassAlignment,
|
||||
bool usualArrayDeleteWantsSize, ArrayRef<Expr*> placementArgs,
|
||||
SourceRange typeIdParens, Expr *arraySize,
|
||||
InitializationStyle initializationStyle, Expr *initializer,
|
||||
QualType ty, TypeSourceInfo *AllocatedTypeInfo,
|
||||
|
@ -1947,10 +1949,16 @@ public:
|
|||
}
|
||||
|
||||
/// \brief Returns the CXXConstructExpr from this new-expression, or null.
|
||||
const CXXConstructExpr* getConstructExpr() const {
|
||||
const CXXConstructExpr *getConstructExpr() const {
|
||||
return dyn_cast_or_null<CXXConstructExpr>(getInitializer());
|
||||
}
|
||||
|
||||
/// Indicates whether the required alignment should be implicitly passed to
|
||||
/// the allocation function.
|
||||
bool passAlignment() const {
|
||||
return PassAlignment;
|
||||
}
|
||||
|
||||
/// Answers whether the usual array deallocation function for the
|
||||
/// allocated type expects the size of the allocation as a
|
||||
/// parameter.
|
||||
|
|
|
@ -1729,7 +1729,8 @@ public:
|
|||
bool isObjCARCBridgableType() const;
|
||||
bool isCARCBridgableType() const;
|
||||
bool isTemplateTypeParmType() const; // C++ template type parameter
|
||||
bool isNullPtrType() const; // C++0x nullptr_t
|
||||
bool isNullPtrType() const; // C++11 std::nullptr_t
|
||||
bool isAlignValT() const; // C++17 std::align_val_t
|
||||
bool isAtomicType() const; // C11 _Atomic()
|
||||
|
||||
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
|
||||
|
|
|
@ -6025,6 +6025,10 @@ def err_no_suitable_delete_member_function_found : Error<
|
|||
"no suitable member %0 in %1">;
|
||||
def err_ambiguous_suitable_delete_member_function_found : Error<
|
||||
"multiple suitable %0 functions in %1">;
|
||||
def warn_ambiguous_suitable_delete_function_found : Warning<
|
||||
"multiple suitable %0 functions for %1; no 'operator delete' function "
|
||||
"will be invoked if initialization throws an exception">,
|
||||
InGroup<DiagGroup<"ambiguous-delete">>;
|
||||
def note_member_declared_here : Note<
|
||||
"member %0 declared here">;
|
||||
def err_decrement_bool : Error<"cannot decrement expression of type bool">;
|
||||
|
|
|
@ -795,7 +795,9 @@ namespace clang {
|
|||
OverloadCandidateDisplayKind OCD,
|
||||
ArrayRef<Expr *> Args,
|
||||
StringRef Opc = "",
|
||||
SourceLocation Loc = SourceLocation());
|
||||
SourceLocation Loc = SourceLocation(),
|
||||
llvm::function_ref<bool(OverloadCandidate&)> Filter =
|
||||
[](OverloadCandidate&) { return true; });
|
||||
};
|
||||
|
||||
bool isBetterOverloadCandidate(Sema &S,
|
||||
|
|
|
@ -4855,14 +4855,9 @@ public:
|
|||
SourceRange R);
|
||||
bool FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
||||
bool UseGlobal, QualType AllocType, bool IsArray,
|
||||
MultiExprArg PlaceArgs,
|
||||
bool &PassAlignment, MultiExprArg PlaceArgs,
|
||||
FunctionDecl *&OperatorNew,
|
||||
FunctionDecl *&OperatorDelete);
|
||||
bool FindAllocationOverload(SourceLocation StartLoc, SourceRange Range,
|
||||
DeclarationName Name, MultiExprArg Args,
|
||||
DeclContext *Ctx,
|
||||
bool AllowMissing, FunctionDecl *&Operator,
|
||||
bool Diagnose = true);
|
||||
void DeclareGlobalNewDelete();
|
||||
void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return,
|
||||
ArrayRef<QualType> Params);
|
||||
|
@ -4872,7 +4867,10 @@ public:
|
|||
bool Diagnose = true);
|
||||
FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
|
||||
bool CanProvideSize,
|
||||
bool Overaligned,
|
||||
DeclarationName Name);
|
||||
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
|
||||
CXXRecordDecl *RD);
|
||||
|
||||
/// ActOnCXXDelete - Parsed a C++ 'delete' expression
|
||||
ExprResult ActOnCXXDelete(SourceLocation StartLoc,
|
||||
|
@ -9337,6 +9335,7 @@ public:
|
|||
void EraseUnwantedCUDAMatches(
|
||||
const FunctionDecl *Caller,
|
||||
SmallVectorImpl<std::pair<DeclAccessPair, FunctionDecl *>> &Matches);
|
||||
void EraseUnwantedCUDAMatches(const FunctionDecl *Caller, LookupResult &R);
|
||||
|
||||
/// Given a implicit special member, infer its CUDA target from the
|
||||
/// calls it needs to make to underlying base/field special members.
|
||||
|
|
|
@ -1572,6 +1572,30 @@ bool ASTContext::isAlignmentRequired(QualType T) const {
|
|||
return isAlignmentRequired(T.getTypePtr());
|
||||
}
|
||||
|
||||
unsigned ASTContext::getTypeAlignIfKnown(QualType T) const {
|
||||
// An alignment on a typedef overrides anything else.
|
||||
if (auto *TT = T->getAs<TypedefType>())
|
||||
if (unsigned Align = TT->getDecl()->getMaxAlignment())
|
||||
return Align;
|
||||
|
||||
// If we have an (array of) complete type, we're done.
|
||||
T = getBaseElementType(T);
|
||||
if (!T->isIncompleteType())
|
||||
return getTypeAlign(T);
|
||||
|
||||
// If we had an array type, its element type might be a typedef
|
||||
// type with an alignment attribute.
|
||||
if (auto *TT = T->getAs<TypedefType>())
|
||||
if (unsigned Align = TT->getDecl()->getMaxAlignment())
|
||||
return Align;
|
||||
|
||||
// Otherwise, see if the declaration of the type had an attribute.
|
||||
if (auto *TT = T->getAs<TagType>())
|
||||
return TT->getDecl()->getMaxAlignment();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TypeInfo ASTContext::getTypeInfo(const Type *T) const {
|
||||
TypeInfoMap::iterator I = MemoizedTypeInfo.find(T);
|
||||
if (I != MemoizedTypeInfo.end())
|
||||
|
|
|
@ -6330,6 +6330,7 @@ Expr *ASTNodeImporter::VisitCXXNewExpr(CXXNewExpr *CE) {
|
|||
Importer.getToContext(),
|
||||
CE->isGlobalNew(),
|
||||
OperatorNewDecl, OperatorDeleteDecl,
|
||||
CE->passAlignment(),
|
||||
CE->doesUsualArrayDeleteWantSize(),
|
||||
PlacementArgs,
|
||||
Importer.Import(CE->getTypeIdParens()),
|
||||
|
|
|
@ -2596,7 +2596,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const {
|
|||
return false;
|
||||
|
||||
const auto *FPT = getType()->castAs<FunctionProtoType>();
|
||||
if (FPT->getNumParams() == 0 || FPT->getNumParams() > 2 || FPT->isVariadic())
|
||||
if (FPT->getNumParams() == 0 || FPT->getNumParams() > 3 || FPT->isVariadic())
|
||||
return false;
|
||||
|
||||
// If this is a single-parameter function, it must be a replaceable global
|
||||
|
@ -2604,20 +2604,42 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const {
|
|||
if (FPT->getNumParams() == 1)
|
||||
return true;
|
||||
|
||||
// Otherwise, we're looking for a second parameter whose type is
|
||||
// 'const std::nothrow_t &', or, in C++1y, 'std::size_t'.
|
||||
QualType Ty = FPT->getParamType(1);
|
||||
unsigned Params = 1;
|
||||
QualType Ty = FPT->getParamType(Params);
|
||||
ASTContext &Ctx = getASTContext();
|
||||
|
||||
auto Consume = [&] {
|
||||
++Params;
|
||||
Ty = Params < FPT->getNumParams() ? FPT->getParamType(Params) : QualType();
|
||||
};
|
||||
|
||||
// In C++14, the next parameter can be a 'std::size_t' for sized delete.
|
||||
bool IsSizedDelete = false;
|
||||
if (Ctx.getLangOpts().SizedDeallocation &&
|
||||
Ctx.hasSameType(Ty, Ctx.getSizeType()))
|
||||
return true;
|
||||
if (!Ty->isReferenceType())
|
||||
return false;
|
||||
Ty = Ty->getPointeeType();
|
||||
if (Ty.getCVRQualifiers() != Qualifiers::Const)
|
||||
return false;
|
||||
const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
|
||||
return RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace();
|
||||
(getDeclName().getCXXOverloadedOperator() == OO_Delete ||
|
||||
getDeclName().getCXXOverloadedOperator() == OO_Array_Delete) &&
|
||||
Ctx.hasSameType(Ty, Ctx.getSizeType())) {
|
||||
IsSizedDelete = true;
|
||||
Consume();
|
||||
}
|
||||
|
||||
// In C++17, the next parameter can be a 'std::align_val_t' for aligned
|
||||
// new/delete.
|
||||
if (Ctx.getLangOpts().AlignedAllocation && !Ty.isNull() && Ty->isAlignValT())
|
||||
Consume();
|
||||
|
||||
// Finally, if this is not a sized delete, the final parameter can
|
||||
// be a 'const std::nothrow_t&'.
|
||||
if (!IsSizedDelete && !Ty.isNull() && Ty->isReferenceType()) {
|
||||
Ty = Ty->getPointeeType();
|
||||
if (Ty.getCVRQualifiers() != Qualifiers::Const)
|
||||
return false;
|
||||
const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
|
||||
if (RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace())
|
||||
Consume();
|
||||
}
|
||||
|
||||
return Params == FPT->getNumParams();
|
||||
}
|
||||
|
||||
LanguageLinkage FunctionDecl::getLanguageLinkage() const {
|
||||
|
|
|
@ -1577,17 +1577,35 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const {
|
|||
// deallocation function. [...]
|
||||
if (getNumParams() == 1)
|
||||
return true;
|
||||
unsigned UsualParams = 1;
|
||||
|
||||
// C++ [basic.stc.dynamic.deallocation]p2:
|
||||
// C++ <=14 [basic.stc.dynamic.deallocation]p2:
|
||||
// [...] If class T does not declare such an operator delete but does
|
||||
// declare a member deallocation function named operator delete with
|
||||
// exactly two parameters, the second of which has type std::size_t (18.1),
|
||||
// then this function is a usual deallocation function.
|
||||
//
|
||||
// C++17 says a usual deallocation function is one with the signature
|
||||
// (void* [, size_t] [, std::align_val_t] [, ...])
|
||||
// and all such functions are usual deallocation functions. It's not clear
|
||||
// that allowing varargs functions was intentional.
|
||||
ASTContext &Context = getASTContext();
|
||||
if (getNumParams() != 2 ||
|
||||
!Context.hasSameUnqualifiedType(getParamDecl(1)->getType(),
|
||||
Context.getSizeType()))
|
||||
if (UsualParams < getNumParams() &&
|
||||
Context.hasSameUnqualifiedType(getParamDecl(UsualParams)->getType(),
|
||||
Context.getSizeType()))
|
||||
++UsualParams;
|
||||
|
||||
if (UsualParams < getNumParams() &&
|
||||
getParamDecl(UsualParams)->getType()->isAlignValT())
|
||||
++UsualParams;
|
||||
|
||||
if (UsualParams != getNumParams())
|
||||
return false;
|
||||
|
||||
// In C++17 onwards, all potential usual deallocation functions are actual
|
||||
// usual deallocation functions.
|
||||
if (Context.getLangOpts().AlignedAllocation)
|
||||
return true;
|
||||
|
||||
// This function is a usual deallocation function if there are no
|
||||
// single-parameter deallocation functions of the same kind.
|
||||
|
|
|
@ -62,7 +62,7 @@ SourceLocation CXXScalarValueInitExpr::getLocStart() const {
|
|||
// CXXNewExpr
|
||||
CXXNewExpr::CXXNewExpr(const ASTContext &C, bool globalNew,
|
||||
FunctionDecl *operatorNew, FunctionDecl *operatorDelete,
|
||||
bool usualArrayDeleteWantsSize,
|
||||
bool PassAlignment, bool usualArrayDeleteWantsSize,
|
||||
ArrayRef<Expr*> placementArgs,
|
||||
SourceRange typeIdParens, Expr *arraySize,
|
||||
InitializationStyle initializationStyle,
|
||||
|
@ -76,7 +76,8 @@ CXXNewExpr::CXXNewExpr(const ASTContext &C, bool globalNew,
|
|||
SubExprs(nullptr), OperatorNew(operatorNew), OperatorDelete(operatorDelete),
|
||||
AllocatedTypeInfo(allocatedTypeInfo), TypeIdParens(typeIdParens),
|
||||
Range(Range), DirectInitRange(directInitRange),
|
||||
GlobalNew(globalNew), UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize) {
|
||||
GlobalNew(globalNew), PassAlignment(PassAlignment),
|
||||
UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize) {
|
||||
assert((initializer != nullptr || initializationStyle == NoInit) &&
|
||||
"Only NoInit can have no initializer.");
|
||||
StoredInitializationStyle = initializer ? initializationStyle + 1 : 0;
|
||||
|
|
|
@ -2337,6 +2337,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Type::isAlignValT() const {
|
||||
if (auto *ET = getAs<EnumType>()) {
|
||||
auto *II = ET->getDecl()->getIdentifier();
|
||||
if (II && II->isStr("align_val_t") && ET->getDecl()->isInStdNamespace())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Type::isPromotableIntegerType() const {
|
||||
if (const BuiltinType *BT = getAs<BuiltinType>())
|
||||
switch (BT->getKind()) {
|
||||
|
|
|
@ -1219,111 +1219,116 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
|
|||
llvm_unreachable("predeclared global operator new/delete is missing");
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// A cleanup to call the given 'operator delete' function upon
|
||||
/// abnormal exit from a new expression.
|
||||
class CallDeleteDuringNew final : public EHScopeStack::Cleanup {
|
||||
size_t NumPlacementArgs;
|
||||
const FunctionDecl *OperatorDelete;
|
||||
llvm::Value *Ptr;
|
||||
llvm::Value *AllocSize;
|
||||
static std::pair<bool, bool>
|
||||
shouldPassSizeAndAlignToUsualDelete(const FunctionProtoType *FPT) {
|
||||
auto AI = FPT->param_type_begin(), AE = FPT->param_type_end();
|
||||
|
||||
RValue *getPlacementArgs() { return reinterpret_cast<RValue*>(this+1); }
|
||||
// The first argument is always a void*.
|
||||
++AI;
|
||||
|
||||
// Figure out what other parameters we should be implicitly passing.
|
||||
bool PassSize = false;
|
||||
bool PassAlignment = false;
|
||||
|
||||
if (AI != AE && (*AI)->isIntegerType()) {
|
||||
PassSize = true;
|
||||
++AI;
|
||||
}
|
||||
|
||||
if (AI != AE && (*AI)->isAlignValT()) {
|
||||
PassAlignment = true;
|
||||
++AI;
|
||||
}
|
||||
|
||||
assert(AI == AE && "unexpected usual deallocation function parameter");
|
||||
return {PassSize, PassAlignment};
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// A cleanup to call the given 'operator delete' function upon abnormal
|
||||
/// exit from a new expression. Templated on a traits type that deals with
|
||||
/// ensuring that the arguments dominate the cleanup if necessary.
|
||||
template<typename Traits>
|
||||
class CallDeleteDuringNew final : public EHScopeStack::Cleanup {
|
||||
/// Type used to hold llvm::Value*s.
|
||||
typedef typename Traits::ValueTy ValueTy;
|
||||
/// Type used to hold RValues.
|
||||
typedef typename Traits::RValueTy RValueTy;
|
||||
struct PlacementArg {
|
||||
RValueTy ArgValue;
|
||||
QualType ArgType;
|
||||
};
|
||||
|
||||
unsigned NumPlacementArgs : 31;
|
||||
unsigned PassAlignmentToPlacementDelete : 1;
|
||||
const FunctionDecl *OperatorDelete;
|
||||
ValueTy Ptr;
|
||||
ValueTy AllocSize;
|
||||
CharUnits AllocAlign;
|
||||
|
||||
PlacementArg *getPlacementArgs() {
|
||||
return reinterpret_cast<PlacementArg *>(this + 1);
|
||||
}
|
||||
|
||||
public:
|
||||
static size_t getExtraSize(size_t NumPlacementArgs) {
|
||||
return NumPlacementArgs * sizeof(RValue);
|
||||
return NumPlacementArgs * sizeof(PlacementArg);
|
||||
}
|
||||
|
||||
CallDeleteDuringNew(size_t NumPlacementArgs,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
llvm::Value *Ptr,
|
||||
llvm::Value *AllocSize)
|
||||
: NumPlacementArgs(NumPlacementArgs), OperatorDelete(OperatorDelete),
|
||||
Ptr(Ptr), AllocSize(AllocSize) {}
|
||||
const FunctionDecl *OperatorDelete, ValueTy Ptr,
|
||||
ValueTy AllocSize, bool PassAlignmentToPlacementDelete,
|
||||
CharUnits AllocAlign)
|
||||
: NumPlacementArgs(NumPlacementArgs),
|
||||
PassAlignmentToPlacementDelete(PassAlignmentToPlacementDelete),
|
||||
OperatorDelete(OperatorDelete), Ptr(Ptr), AllocSize(AllocSize),
|
||||
AllocAlign(AllocAlign) {}
|
||||
|
||||
void setPlacementArg(unsigned I, RValue Arg) {
|
||||
void setPlacementArg(unsigned I, RValueTy Arg, QualType Type) {
|
||||
assert(I < NumPlacementArgs && "index out of range");
|
||||
getPlacementArgs()[I] = Arg;
|
||||
getPlacementArgs()[I] = {Arg, Type};
|
||||
}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
const FunctionProtoType *FPT
|
||||
= OperatorDelete->getType()->getAs<FunctionProtoType>();
|
||||
assert(FPT->getNumParams() == NumPlacementArgs + 1 ||
|
||||
(FPT->getNumParams() == 2 && NumPlacementArgs == 0));
|
||||
|
||||
const FunctionProtoType *FPT =
|
||||
OperatorDelete->getType()->getAs<FunctionProtoType>();
|
||||
CallArgList DeleteArgs;
|
||||
|
||||
// The first argument is always a void*.
|
||||
FunctionProtoType::param_type_iterator AI = FPT->param_type_begin();
|
||||
DeleteArgs.add(RValue::get(Ptr), *AI++);
|
||||
DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0));
|
||||
|
||||
// A member 'operator delete' can take an extra 'size_t' argument.
|
||||
if (FPT->getNumParams() == NumPlacementArgs + 2)
|
||||
DeleteArgs.add(RValue::get(AllocSize), *AI++);
|
||||
|
||||
// Pass the rest of the arguments, which must match exactly.
|
||||
for (unsigned I = 0; I != NumPlacementArgs; ++I)
|
||||
DeleteArgs.add(getPlacementArgs()[I], *AI++);
|
||||
|
||||
// Call 'operator delete'.
|
||||
EmitNewDeleteCall(CGF, OperatorDelete, FPT, DeleteArgs);
|
||||
}
|
||||
};
|
||||
|
||||
/// A cleanup to call the given 'operator delete' function upon
|
||||
/// abnormal exit from a new expression when the new expression is
|
||||
/// conditional.
|
||||
class CallDeleteDuringConditionalNew final : public EHScopeStack::Cleanup {
|
||||
size_t NumPlacementArgs;
|
||||
const FunctionDecl *OperatorDelete;
|
||||
DominatingValue<RValue>::saved_type Ptr;
|
||||
DominatingValue<RValue>::saved_type AllocSize;
|
||||
|
||||
DominatingValue<RValue>::saved_type *getPlacementArgs() {
|
||||
return reinterpret_cast<DominatingValue<RValue>::saved_type*>(this+1);
|
||||
}
|
||||
|
||||
public:
|
||||
static size_t getExtraSize(size_t NumPlacementArgs) {
|
||||
return NumPlacementArgs * sizeof(DominatingValue<RValue>::saved_type);
|
||||
}
|
||||
|
||||
CallDeleteDuringConditionalNew(size_t NumPlacementArgs,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
DominatingValue<RValue>::saved_type Ptr,
|
||||
DominatingValue<RValue>::saved_type AllocSize)
|
||||
: NumPlacementArgs(NumPlacementArgs), OperatorDelete(OperatorDelete),
|
||||
Ptr(Ptr), AllocSize(AllocSize) {}
|
||||
|
||||
void setPlacementArg(unsigned I, DominatingValue<RValue>::saved_type Arg) {
|
||||
assert(I < NumPlacementArgs && "index out of range");
|
||||
getPlacementArgs()[I] = Arg;
|
||||
}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
const FunctionProtoType *FPT
|
||||
= OperatorDelete->getType()->getAs<FunctionProtoType>();
|
||||
assert(FPT->getNumParams() == NumPlacementArgs + 1 ||
|
||||
(FPT->getNumParams() == 2 && NumPlacementArgs == 0));
|
||||
|
||||
CallArgList DeleteArgs;
|
||||
|
||||
// The first argument is always a void*.
|
||||
FunctionProtoType::param_type_iterator AI = FPT->param_type_begin();
|
||||
DeleteArgs.add(Ptr.restore(CGF), *AI++);
|
||||
|
||||
// A member 'operator delete' can take an extra 'size_t' argument.
|
||||
if (FPT->getNumParams() == NumPlacementArgs + 2) {
|
||||
RValue RV = AllocSize.restore(CGF);
|
||||
DeleteArgs.add(RV, *AI++);
|
||||
// Figure out what other parameters we should be implicitly passing.
|
||||
bool PassSize = false;
|
||||
bool PassAlignment = false;
|
||||
if (NumPlacementArgs) {
|
||||
// A placement deallocation function is implicitly passed an alignment
|
||||
// if the placement allocation function was, but is never passed a size.
|
||||
PassAlignment = PassAlignmentToPlacementDelete;
|
||||
} else {
|
||||
// For a non-placement new-expression, 'operator delete' can take a
|
||||
// size and/or an alignment if it has the right parameters.
|
||||
std::tie(PassSize, PassAlignment) =
|
||||
shouldPassSizeAndAlignToUsualDelete(FPT);
|
||||
}
|
||||
|
||||
// The second argument can be a std::size_t (for non-placement delete).
|
||||
if (PassSize)
|
||||
DeleteArgs.add(Traits::get(CGF, AllocSize),
|
||||
CGF.getContext().getSizeType());
|
||||
|
||||
// The next (second or third) argument can be a std::align_val_t, which
|
||||
// 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 (PassAlignment)
|
||||
DeleteArgs.add(RValue::get(llvm::ConstantInt::get(
|
||||
CGF.SizeTy, AllocAlign.getQuantity())),
|
||||
CGF.getContext().getSizeType());
|
||||
|
||||
// Pass the rest of the arguments, which must match exactly.
|
||||
for (unsigned I = 0; I != NumPlacementArgs; ++I) {
|
||||
RValue RV = getPlacementArgs()[I].restore(CGF);
|
||||
DeleteArgs.add(RV, *AI++);
|
||||
auto Arg = getPlacementArgs()[I];
|
||||
DeleteArgs.add(Traits::get(CGF, Arg.ArgValue), Arg.ArgType);
|
||||
}
|
||||
|
||||
// Call 'operator delete'.
|
||||
|
@ -1338,18 +1343,34 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
|
|||
const CXXNewExpr *E,
|
||||
Address NewPtr,
|
||||
llvm::Value *AllocSize,
|
||||
CharUnits AllocAlign,
|
||||
const CallArgList &NewArgs) {
|
||||
unsigned NumNonPlacementArgs = E->passAlignment() ? 2 : 1;
|
||||
|
||||
// If we're not inside a conditional branch, then the cleanup will
|
||||
// dominate and we can do the easier (and more efficient) thing.
|
||||
if (!CGF.isInConditionalBranch()) {
|
||||
CallDeleteDuringNew *Cleanup = CGF.EHStack
|
||||
.pushCleanupWithExtra<CallDeleteDuringNew>(EHCleanup,
|
||||
E->getNumPlacementArgs(),
|
||||
E->getOperatorDelete(),
|
||||
NewPtr.getPointer(),
|
||||
AllocSize);
|
||||
for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I)
|
||||
Cleanup->setPlacementArg(I, NewArgs[I+1].RV);
|
||||
struct DirectCleanupTraits {
|
||||
typedef llvm::Value *ValueTy;
|
||||
typedef RValue RValueTy;
|
||||
static RValue get(CodeGenFunction &, ValueTy V) { return RValue::get(V); }
|
||||
static RValue get(CodeGenFunction &, RValueTy V) { return V; }
|
||||
};
|
||||
|
||||
typedef CallDeleteDuringNew<DirectCleanupTraits> DirectCleanup;
|
||||
|
||||
DirectCleanup *Cleanup = CGF.EHStack
|
||||
.pushCleanupWithExtra<DirectCleanup>(EHCleanup,
|
||||
E->getNumPlacementArgs(),
|
||||
E->getOperatorDelete(),
|
||||
NewPtr.getPointer(),
|
||||
AllocSize,
|
||||
E->passAlignment(),
|
||||
AllocAlign);
|
||||
for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
|
||||
auto &Arg = NewArgs[I + NumNonPlacementArgs];
|
||||
Cleanup->setPlacementArg(I, Arg.RV, Arg.Ty);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1360,15 +1381,28 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF,
|
|||
DominatingValue<RValue>::saved_type SavedAllocSize =
|
||||
DominatingValue<RValue>::save(CGF, RValue::get(AllocSize));
|
||||
|
||||
CallDeleteDuringConditionalNew *Cleanup = CGF.EHStack
|
||||
.pushCleanupWithExtra<CallDeleteDuringConditionalNew>(EHCleanup,
|
||||
E->getNumPlacementArgs(),
|
||||
E->getOperatorDelete(),
|
||||
SavedNewPtr,
|
||||
SavedAllocSize);
|
||||
for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I)
|
||||
Cleanup->setPlacementArg(I,
|
||||
DominatingValue<RValue>::save(CGF, NewArgs[I+1].RV));
|
||||
struct ConditionalCleanupTraits {
|
||||
typedef DominatingValue<RValue>::saved_type ValueTy;
|
||||
typedef DominatingValue<RValue>::saved_type RValueTy;
|
||||
static RValue get(CodeGenFunction &CGF, ValueTy V) {
|
||||
return V.restore(CGF);
|
||||
}
|
||||
};
|
||||
typedef CallDeleteDuringNew<ConditionalCleanupTraits> ConditionalCleanup;
|
||||
|
||||
ConditionalCleanup *Cleanup = CGF.EHStack
|
||||
.pushCleanupWithExtra<ConditionalCleanup>(EHCleanup,
|
||||
E->getNumPlacementArgs(),
|
||||
E->getOperatorDelete(),
|
||||
SavedNewPtr,
|
||||
SavedAllocSize,
|
||||
E->passAlignment(),
|
||||
AllocAlign);
|
||||
for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) {
|
||||
auto &Arg = NewArgs[I + NumNonPlacementArgs];
|
||||
Cleanup->setPlacementArg(I, DominatingValue<RValue>::save(CGF, Arg.RV),
|
||||
Arg.Ty);
|
||||
}
|
||||
|
||||
CGF.initFullExprCleanup();
|
||||
}
|
||||
|
@ -1397,6 +1431,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
|||
llvm::Value *allocSize =
|
||||
EmitCXXNewAllocSize(*this, E, minElements, numElements,
|
||||
allocSizeWithoutCookie);
|
||||
CharUnits allocAlign = getContext().getTypeAlignInChars(allocType);
|
||||
|
||||
// Emit the allocation call. If the allocator is a global placement
|
||||
// operator, just "inline" it directly.
|
||||
|
@ -1412,10 +1447,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
|||
// The pointer expression will, in many cases, be an opaque void*.
|
||||
// In these cases, discard the computed alignment and use the
|
||||
// formal alignment of the allocated type.
|
||||
if (alignSource != AlignmentSource::Decl) {
|
||||
allocation = Address(allocation.getPointer(),
|
||||
getContext().getTypeAlignInChars(allocType));
|
||||
}
|
||||
if (alignSource != AlignmentSource::Decl)
|
||||
allocation = Address(allocation.getPointer(), allocAlign);
|
||||
|
||||
// Set up allocatorArgs for the call to operator delete if it's not
|
||||
// the reserved global operator.
|
||||
|
@ -1428,28 +1461,55 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
|||
} else {
|
||||
const FunctionProtoType *allocatorType =
|
||||
allocator->getType()->castAs<FunctionProtoType>();
|
||||
unsigned ParamsToSkip = 0;
|
||||
|
||||
// The allocation size is the first argument.
|
||||
QualType sizeType = getContext().getSizeType();
|
||||
allocatorArgs.add(RValue::get(allocSize), sizeType);
|
||||
++ParamsToSkip;
|
||||
|
||||
// We start at 1 here because the first argument (the allocation size)
|
||||
// has already been emitted.
|
||||
if (allocSize != allocSizeWithoutCookie) {
|
||||
CharUnits cookieAlign = getSizeAlign(); // FIXME: Ask the ABI.
|
||||
allocAlign = std::max(allocAlign, cookieAlign);
|
||||
}
|
||||
|
||||
// The allocation alignment may be passed as the second argument.
|
||||
if (E->passAlignment()) {
|
||||
QualType AlignValT = sizeType;
|
||||
if (allocatorType->getNumParams() > 1) {
|
||||
AlignValT = allocatorType->getParamType(1);
|
||||
assert(getContext().hasSameUnqualifiedType(
|
||||
AlignValT->castAs<EnumType>()->getDecl()->getIntegerType(),
|
||||
sizeType) &&
|
||||
"wrong type for alignment parameter");
|
||||
++ParamsToSkip;
|
||||
} else {
|
||||
// Corner case, passing alignment to 'operator new(size_t, ...)'.
|
||||
assert(allocator->isVariadic() && "can't pass alignment to allocator");
|
||||
}
|
||||
allocatorArgs.add(
|
||||
RValue::get(llvm::ConstantInt::get(SizeTy, allocAlign.getQuantity())),
|
||||
AlignValT);
|
||||
}
|
||||
|
||||
// FIXME: Why do we not pass a CalleeDecl here?
|
||||
EmitCallArgs(allocatorArgs, allocatorType, E->placement_arguments(),
|
||||
/* CalleeDecl */ nullptr,
|
||||
/*ParamsToSkip*/ 1);
|
||||
/*CalleeDecl*/nullptr, /*ParamsToSkip*/ParamsToSkip);
|
||||
|
||||
RValue RV =
|
||||
EmitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs);
|
||||
|
||||
// For now, only assume that the allocation function returns
|
||||
// something satisfactorily aligned for the element type, plus
|
||||
// the cookie if we have one.
|
||||
CharUnits allocationAlign =
|
||||
getContext().getTypeAlignInChars(allocType);
|
||||
if (allocSize != allocSizeWithoutCookie) {
|
||||
CharUnits cookieAlign = getSizeAlign(); // FIXME?
|
||||
allocationAlign = std::max(allocationAlign, cookieAlign);
|
||||
// If this was a call to a global replaceable allocation function that does
|
||||
// not take an alignment argument, the allocator is known to produce
|
||||
// storage that's suitably aligned for any object that fits, up to a known
|
||||
// threshold. Otherwise assume it's suitably aligned for the allocated type.
|
||||
CharUnits allocationAlign = allocAlign;
|
||||
if (!E->passAlignment() &&
|
||||
allocator->isReplaceableGlobalAllocationFunction()) {
|
||||
unsigned AllocatorAlign = llvm::PowerOf2Floor(std::min<uint64_t>(
|
||||
Target.getNewAlign(), getContext().getTypeSize(allocType)));
|
||||
allocationAlign = std::max(
|
||||
allocationAlign, getContext().toCharUnitsFromBits(AllocatorAlign));
|
||||
}
|
||||
|
||||
allocation = Address(RV.getScalarVal(), allocationAlign);
|
||||
|
@ -1488,7 +1548,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
|||
llvm::Instruction *cleanupDominator = nullptr;
|
||||
if (E->getOperatorDelete() &&
|
||||
!E->getOperatorDelete()->isReservedGlobalPlacementOperator()) {
|
||||
EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocatorArgs);
|
||||
EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocAlign,
|
||||
allocatorArgs);
|
||||
operatorDeleteCleanup = EHStack.stable_begin();
|
||||
cleanupDominator = Builder.CreateUnreachable();
|
||||
}
|
||||
|
@ -1550,31 +1611,58 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
|||
}
|
||||
|
||||
void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
|
||||
llvm::Value *Ptr,
|
||||
QualType DeleteTy) {
|
||||
assert(DeleteFD->getOverloadedOperator() == OO_Delete);
|
||||
llvm::Value *Ptr, QualType DeleteTy,
|
||||
llvm::Value *NumElements,
|
||||
CharUnits CookieSize) {
|
||||
assert((!NumElements && CookieSize.isZero()) ||
|
||||
DeleteFD->getOverloadedOperator() == OO_Array_Delete);
|
||||
|
||||
const FunctionProtoType *DeleteFTy =
|
||||
DeleteFD->getType()->getAs<FunctionProtoType>();
|
||||
|
||||
CallArgList DeleteArgs;
|
||||
|
||||
// Check if we need to pass the size to the delete operator.
|
||||
llvm::Value *Size = nullptr;
|
||||
QualType SizeTy;
|
||||
if (DeleteFTy->getNumParams() == 2) {
|
||||
SizeTy = DeleteFTy->getParamType(1);
|
||||
CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy);
|
||||
Size = llvm::ConstantInt::get(ConvertType(SizeTy),
|
||||
DeleteTypeSize.getQuantity());
|
||||
}
|
||||
std::pair<bool, bool> PassSizeAndAlign =
|
||||
shouldPassSizeAndAlignToUsualDelete(DeleteFTy);
|
||||
|
||||
QualType ArgTy = DeleteFTy->getParamType(0);
|
||||
auto ParamTypeIt = DeleteFTy->param_type_begin();
|
||||
|
||||
// Pass the pointer itself.
|
||||
QualType ArgTy = *ParamTypeIt++;
|
||||
llvm::Value *DeletePtr = Builder.CreateBitCast(Ptr, ConvertType(ArgTy));
|
||||
DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
|
||||
|
||||
if (Size)
|
||||
DeleteArgs.add(RValue::get(Size), SizeTy);
|
||||
// Pass the size if the delete function has a size_t parameter.
|
||||
if (PassSizeAndAlign.first) {
|
||||
QualType SizeType = *ParamTypeIt++;
|
||||
CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy);
|
||||
llvm::Value *Size = llvm::ConstantInt::get(ConvertType(SizeType),
|
||||
DeleteTypeSize.getQuantity());
|
||||
|
||||
// For array new, multiply by the number of elements.
|
||||
if (NumElements)
|
||||
Size = Builder.CreateMul(Size, NumElements);
|
||||
|
||||
// If there is a cookie, add the cookie size.
|
||||
if (!CookieSize.isZero())
|
||||
Size = Builder.CreateAdd(
|
||||
Size, llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity()));
|
||||
|
||||
DeleteArgs.add(RValue::get(Size), SizeType);
|
||||
}
|
||||
|
||||
// Pass the alignment if the delete function has an align_val_t parameter.
|
||||
if (PassSizeAndAlign.second) {
|
||||
QualType AlignValType = *ParamTypeIt++;
|
||||
CharUnits DeleteTypeAlign = getContext().toCharUnitsFromBits(
|
||||
getContext().getTypeAlignIfKnown(DeleteTy));
|
||||
llvm::Value *Align = llvm::ConstantInt::get(ConvertType(AlignValType),
|
||||
DeleteTypeAlign.getQuantity());
|
||||
DeleteArgs.add(RValue::get(Align), AlignValType);
|
||||
}
|
||||
|
||||
assert(ParamTypeIt == DeleteFTy->param_type_end() &&
|
||||
"unknown parameter to usual delete function");
|
||||
|
||||
// Emit the call to delete.
|
||||
EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs);
|
||||
|
@ -1678,45 +1766,8 @@ namespace {
|
|||
ElementType(ElementType), CookieSize(CookieSize) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
const FunctionProtoType *DeleteFTy =
|
||||
OperatorDelete->getType()->getAs<FunctionProtoType>();
|
||||
assert(DeleteFTy->getNumParams() == 1 || DeleteFTy->getNumParams() == 2);
|
||||
|
||||
CallArgList Args;
|
||||
|
||||
// Pass the pointer as the first argument.
|
||||
QualType VoidPtrTy = DeleteFTy->getParamType(0);
|
||||
llvm::Value *DeletePtr
|
||||
= CGF.Builder.CreateBitCast(Ptr, CGF.ConvertType(VoidPtrTy));
|
||||
Args.add(RValue::get(DeletePtr), VoidPtrTy);
|
||||
|
||||
// Pass the original requested size as the second argument.
|
||||
if (DeleteFTy->getNumParams() == 2) {
|
||||
QualType size_t = DeleteFTy->getParamType(1);
|
||||
llvm::IntegerType *SizeTy
|
||||
= cast<llvm::IntegerType>(CGF.ConvertType(size_t));
|
||||
|
||||
CharUnits ElementTypeSize =
|
||||
CGF.CGM.getContext().getTypeSizeInChars(ElementType);
|
||||
|
||||
// The size of an element, multiplied by the number of elements.
|
||||
llvm::Value *Size
|
||||
= llvm::ConstantInt::get(SizeTy, ElementTypeSize.getQuantity());
|
||||
if (NumElements)
|
||||
Size = CGF.Builder.CreateMul(Size, NumElements);
|
||||
|
||||
// Plus the size of the cookie if applicable.
|
||||
if (!CookieSize.isZero()) {
|
||||
llvm::Value *CookieSizeV
|
||||
= llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity());
|
||||
Size = CGF.Builder.CreateAdd(Size, CookieSizeV);
|
||||
}
|
||||
|
||||
Args.add(RValue::get(Size), size_t);
|
||||
}
|
||||
|
||||
// Emit the call to delete.
|
||||
EmitNewDeleteCall(CGF, OperatorDelete, DeleteFTy, Args);
|
||||
CGF.EmitDeleteCall(OperatorDelete, Ptr, ElementType, NumElements,
|
||||
CookieSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2033,7 +2033,8 @@ public:
|
|||
void EmitCXXDeleteExpr(const CXXDeleteExpr *E);
|
||||
|
||||
void EmitDeleteCall(const FunctionDecl *DeleteFD, llvm::Value *Ptr,
|
||||
QualType DeleteTy);
|
||||
QualType DeleteTy, llvm::Value *NumElements = nullptr,
|
||||
CharUnits CookieSize = CharUnits());
|
||||
|
||||
RValue EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
|
||||
const Expr *Arg, bool IsDelete);
|
||||
|
|
|
@ -158,6 +158,34 @@ Sema::IdentifyCUDAPreference(const FunctionDecl *Caller,
|
|||
llvm_unreachable("All cases should've been handled by now.");
|
||||
}
|
||||
|
||||
void Sema::EraseUnwantedCUDAMatches(const FunctionDecl *Caller,
|
||||
LookupResult &R) {
|
||||
if (R.isSingleResult())
|
||||
return;
|
||||
|
||||
// Gets the CUDA function preference for a call from Caller to Match.
|
||||
auto GetCFP = [&](const NamedDecl *D) {
|
||||
if (auto *Callee = dyn_cast<FunctionDecl>(D->getUnderlyingDecl()))
|
||||
return IdentifyCUDAPreference(Caller, Callee);
|
||||
return CFP_Never;
|
||||
};
|
||||
|
||||
// Find the best call preference among the functions in R.
|
||||
CUDAFunctionPreference BestCFP = GetCFP(*std::max_element(
|
||||
R.begin(), R.end(), [&](const NamedDecl *D1, const NamedDecl *D2) {
|
||||
return GetCFP(D1) < GetCFP(D2);
|
||||
}));
|
||||
|
||||
// Erase all functions with lower priority.
|
||||
auto Filter = R.makeFilter();
|
||||
while (Filter.hasNext()) {
|
||||
auto *Callee = dyn_cast<FunctionDecl>(Filter.next()->getUnderlyingDecl());
|
||||
if (Callee && GetCFP(Callee) < BestCFP)
|
||||
Filter.erase();
|
||||
}
|
||||
Filter.done();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void EraseUnwantedCUDAMatchesImpl(
|
||||
Sema &S, const FunctionDecl *Caller, llvm::SmallVectorImpl<T> &Matches,
|
||||
|
|
|
@ -14368,6 +14368,14 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
|
|||
if (!Completed)
|
||||
Record->completeDefinition();
|
||||
|
||||
// We may have deferred checking for a deleted destructor. Check now.
|
||||
if (CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(Record)) {
|
||||
auto *Dtor = CXXRecord->getDestructor();
|
||||
if (Dtor && Dtor->isImplicit() &&
|
||||
ShouldDeleteSpecialMember(Dtor, CXXDestructor))
|
||||
SetDeclDeleted(Dtor, CXXRecord->getLocation());
|
||||
}
|
||||
|
||||
if (Record->hasAttrs()) {
|
||||
CheckAlignasUnderalignment(Record);
|
||||
|
||||
|
|
|
@ -6755,7 +6755,7 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,
|
|||
DeclarationName Name =
|
||||
Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
|
||||
OperatorDelete, false)) {
|
||||
OperatorDelete, /*Diagnose*/false)) {
|
||||
if (Diagnose)
|
||||
Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete);
|
||||
return true;
|
||||
|
@ -7695,19 +7695,11 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
|
|||
Loc = RD->getLocation();
|
||||
|
||||
// If we have a virtual destructor, look up the deallocation function
|
||||
FunctionDecl *OperatorDelete = nullptr;
|
||||
DeclarationName Name =
|
||||
Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete))
|
||||
return true;
|
||||
// If there's no class-specific operator delete, look up the global
|
||||
// non-array delete.
|
||||
if (!OperatorDelete)
|
||||
OperatorDelete = FindUsualDeallocationFunction(Loc, true, Name);
|
||||
|
||||
MarkFunctionReferenced(Loc, OperatorDelete);
|
||||
|
||||
Destructor->setOperatorDelete(OperatorDelete);
|
||||
if (FunctionDecl *OperatorDelete =
|
||||
FindDeallocationFunctionForDestructor(Loc, RD)) {
|
||||
MarkFunctionReferenced(Loc, OperatorDelete);
|
||||
Destructor->setOperatorDelete(OperatorDelete);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -10280,7 +10272,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
|
|||
Scope *S = getScopeForContext(ClassDecl);
|
||||
CheckImplicitSpecialMemberDeclaration(S, Destructor);
|
||||
|
||||
if (ShouldDeleteSpecialMember(Destructor, CXXDestructor))
|
||||
// We can't check whether an implicit destructor is deleted before we complete
|
||||
// the definition of the class, because its validity depends on the alignment
|
||||
// of the class. We'll check this from ActOnFields once the class is complete.
|
||||
if (ClassDecl->isCompleteDefinition() &&
|
||||
ShouldDeleteSpecialMember(Destructor, CXXDestructor))
|
||||
SetDeclDeleted(Destructor, ClassLoc);
|
||||
|
||||
// Introduce this destructor into its scope.
|
||||
|
|
|
@ -1321,8 +1321,126 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
|
|||
return Result;
|
||||
}
|
||||
|
||||
/// doesUsualArrayDeleteWantSize - Answers whether the usual
|
||||
/// operator delete[] for the given type has a size_t parameter.
|
||||
/// \brief Determine whether the given function is a non-placement
|
||||
/// deallocation function.
|
||||
static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
|
||||
if (FD->isInvalidDecl())
|
||||
return false;
|
||||
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD))
|
||||
return Method->isUsualDeallocationFunction();
|
||||
|
||||
if (FD->getOverloadedOperator() != OO_Delete &&
|
||||
FD->getOverloadedOperator() != OO_Array_Delete)
|
||||
return false;
|
||||
|
||||
unsigned UsualParams = 1;
|
||||
|
||||
if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() &&
|
||||
S.Context.hasSameUnqualifiedType(
|
||||
FD->getParamDecl(UsualParams)->getType(),
|
||||
S.Context.getSizeType()))
|
||||
++UsualParams;
|
||||
|
||||
if (S.getLangOpts().AlignedAllocation && UsualParams < FD->getNumParams() &&
|
||||
S.Context.hasSameUnqualifiedType(
|
||||
FD->getParamDecl(UsualParams)->getType(),
|
||||
S.Context.getTypeDeclType(S.getStdAlignValT())))
|
||||
++UsualParams;
|
||||
|
||||
return UsualParams == FD->getNumParams();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct UsualDeallocFnInfo {
|
||||
UsualDeallocFnInfo() : Found(), FD(nullptr) {}
|
||||
UsualDeallocFnInfo(DeclAccessPair Found)
|
||||
: Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
|
||||
HasSizeT(false), HasAlignValT(false) {
|
||||
// A function template declaration is never a usual deallocation function.
|
||||
if (!FD)
|
||||
return;
|
||||
if (FD->getNumParams() == 3)
|
||||
HasAlignValT = HasSizeT = true;
|
||||
else if (FD->getNumParams() == 2) {
|
||||
HasSizeT = FD->getParamDecl(1)->getType()->isIntegerType();
|
||||
HasAlignValT = !HasSizeT;
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const { return FD; }
|
||||
|
||||
DeclAccessPair Found;
|
||||
FunctionDecl *FD;
|
||||
bool HasSizeT, HasAlignValT;
|
||||
};
|
||||
}
|
||||
|
||||
/// Determine whether a type has new-extended alignment. This may be called when
|
||||
/// the type is incomplete (for a delete-expression with an incomplete pointee
|
||||
/// type), in which case it will conservatively return false if the alignment is
|
||||
/// not known.
|
||||
static bool hasNewExtendedAlignment(Sema &S, QualType AllocType) {
|
||||
return S.getLangOpts().AlignedAllocation &&
|
||||
S.getASTContext().getTypeAlignIfKnown(AllocType) >
|
||||
S.getASTContext().getTargetInfo().getNewAlign();
|
||||
}
|
||||
|
||||
/// Select the correct "usual" deallocation function to use from a selection of
|
||||
/// deallocation functions (either global or class-scope).
|
||||
static UsualDeallocFnInfo resolveDeallocationOverload(
|
||||
Sema &S, LookupResult &R, bool WantSize, bool WantAlign,
|
||||
llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) {
|
||||
UsualDeallocFnInfo Best;
|
||||
|
||||
// For CUDA, rank callability above anything else when ordering usual
|
||||
// deallocation functions.
|
||||
// FIXME: We should probably instead rank this between alignment (which
|
||||
// affects correctness) and size (which is just an optimization).
|
||||
if (S.getLangOpts().CUDA)
|
||||
S.EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(S.CurContext), R);
|
||||
|
||||
for (auto I = R.begin(), E = R.end(); I != E; ++I) {
|
||||
UsualDeallocFnInfo Info(I.getPair());
|
||||
if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD))
|
||||
continue;
|
||||
|
||||
if (!Best) {
|
||||
Best = Info;
|
||||
if (BestFns)
|
||||
BestFns->push_back(Info);
|
||||
continue;
|
||||
}
|
||||
|
||||
// C++17 [expr.delete]p10:
|
||||
// If the type has new-extended alignment, a function with a parameter of
|
||||
// type std::align_val_t is preferred; otherwise a function without such a
|
||||
// parameter is preferred
|
||||
if (Best.HasAlignValT == WantAlign && Info.HasAlignValT != WantAlign)
|
||||
continue;
|
||||
|
||||
if (Best.HasAlignValT == Info.HasAlignValT &&
|
||||
Best.HasSizeT == WantSize && Info.HasSizeT != WantSize)
|
||||
continue;
|
||||
|
||||
// If more than one preferred function is found, all non-preferred
|
||||
// functions are eliminated from further consideration.
|
||||
if (BestFns && (Best.HasAlignValT != Info.HasAlignValT ||
|
||||
Best.HasSizeT != Info.HasSizeT))
|
||||
BestFns->clear();
|
||||
|
||||
Best = Info;
|
||||
if (BestFns)
|
||||
BestFns->push_back(Info);
|
||||
}
|
||||
|
||||
return Best;
|
||||
}
|
||||
|
||||
/// Determine whether a given type is a class for which 'delete[]' would call
|
||||
/// a member 'operator delete[]' with a 'size_t' parameter. This implies that
|
||||
/// we need to store the array size (even if the type is
|
||||
/// trivially-destructible).
|
||||
static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
|
||||
QualType allocType) {
|
||||
const RecordType *record =
|
||||
|
@ -1346,35 +1464,13 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
|
|||
// on this thing, so it doesn't matter if we allocate extra space or not.
|
||||
if (ops.isAmbiguous()) return false;
|
||||
|
||||
LookupResult::Filter filter = ops.makeFilter();
|
||||
while (filter.hasNext()) {
|
||||
NamedDecl *del = filter.next()->getUnderlyingDecl();
|
||||
|
||||
// C++0x [basic.stc.dynamic.deallocation]p2:
|
||||
// A template instance is never a usual deallocation function,
|
||||
// regardless of its signature.
|
||||
if (isa<FunctionTemplateDecl>(del)) {
|
||||
filter.erase();
|
||||
continue;
|
||||
}
|
||||
|
||||
// C++0x [basic.stc.dynamic.deallocation]p2:
|
||||
// If class T does not declare [an operator delete[] with one
|
||||
// parameter] but does declare a member deallocation function
|
||||
// named operator delete[] with exactly two parameters, the
|
||||
// second of which has type std::size_t, then this function
|
||||
// is a usual deallocation function.
|
||||
if (!cast<CXXMethodDecl>(del)->isUsualDeallocationFunction()) {
|
||||
filter.erase();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
filter.done();
|
||||
|
||||
if (!ops.isSingleResult()) return false;
|
||||
|
||||
const FunctionDecl *del = cast<FunctionDecl>(ops.getFoundDecl());
|
||||
return (del->getNumParams() == 2);
|
||||
// C++17 [expr.delete]p10:
|
||||
// If the deallocation functions have class scope, the one without a
|
||||
// parameter of type std::size_t is selected.
|
||||
auto Best = resolveDeallocationOverload(
|
||||
S, ops, /*WantSize*/false,
|
||||
/*WantAlign*/hasNewExtendedAlignment(S, allocType));
|
||||
return Best && Best.HasSizeT;
|
||||
}
|
||||
|
||||
/// \brief Parsed a C++ 'new' expression (C++ 5.3.4).
|
||||
|
@ -1730,21 +1826,26 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
|
|||
|
||||
FunctionDecl *OperatorNew = nullptr;
|
||||
FunctionDecl *OperatorDelete = nullptr;
|
||||
unsigned Alignment =
|
||||
AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
|
||||
unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
|
||||
bool PassAlignment = getLangOpts().AlignedAllocation &&
|
||||
Alignment > NewAlignment;
|
||||
|
||||
if (!AllocType->isDependentType() &&
|
||||
!Expr::hasAnyTypeDependentArguments(PlacementArgs) &&
|
||||
FindAllocationFunctions(StartLoc,
|
||||
SourceRange(PlacementLParen, PlacementRParen),
|
||||
UseGlobal, AllocType, ArraySize, PlacementArgs,
|
||||
OperatorNew, OperatorDelete))
|
||||
UseGlobal, AllocType, ArraySize, PassAlignment,
|
||||
PlacementArgs, OperatorNew, OperatorDelete))
|
||||
return ExprError();
|
||||
|
||||
// If this is an array allocation, compute whether the usual array
|
||||
// deallocation function for the type has a size_t parameter.
|
||||
bool UsualArrayDeleteWantsSize = false;
|
||||
if (ArraySize && !AllocType->isDependentType())
|
||||
UsualArrayDeleteWantsSize
|
||||
= doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType);
|
||||
UsualArrayDeleteWantsSize =
|
||||
doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType);
|
||||
|
||||
SmallVector<Expr *, 8> AllPlaceArgs;
|
||||
if (OperatorNew) {
|
||||
|
@ -1755,9 +1856,11 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
|
|||
|
||||
// We've already converted the placement args, just fill in any default
|
||||
// arguments. Skip the first parameter because we don't have a corresponding
|
||||
// argument.
|
||||
if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto, 1,
|
||||
PlacementArgs, AllPlaceArgs, CallType))
|
||||
// argument. Skip the second parameter too if we're passing in the
|
||||
// alignment; we've already filled it in.
|
||||
if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto,
|
||||
PassAlignment ? 2 : 1, PlacementArgs,
|
||||
AllPlaceArgs, CallType))
|
||||
return ExprError();
|
||||
|
||||
if (!AllPlaceArgs.empty())
|
||||
|
@ -1767,21 +1870,18 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
|
|||
DiagnoseSentinelCalls(OperatorNew, PlacementLParen, PlacementArgs);
|
||||
|
||||
// FIXME: Missing call to CheckFunctionCall or equivalent
|
||||
}
|
||||
|
||||
// Warn if the type is over-aligned and is being allocated by global operator
|
||||
// new.
|
||||
if (PlacementArgs.empty() && OperatorNew &&
|
||||
(OperatorNew->isImplicit() ||
|
||||
(OperatorNew->getLocStart().isValid() &&
|
||||
getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) {
|
||||
if (unsigned Align = Context.getPreferredTypeAlign(AllocType.getTypePtr())){
|
||||
unsigned SuitableAlign = Context.getTargetInfo().getSuitableAlign();
|
||||
if (Align > SuitableAlign)
|
||||
// Warn if the type is over-aligned and is being allocated by (unaligned)
|
||||
// global operator new.
|
||||
if (PlacementArgs.empty() && !PassAlignment &&
|
||||
(OperatorNew->isImplicit() ||
|
||||
(OperatorNew->getLocStart().isValid() &&
|
||||
getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) {
|
||||
if (Alignment > NewAlignment)
|
||||
Diag(StartLoc, diag::warn_overaligned_type)
|
||||
<< AllocType
|
||||
<< unsigned(Align / Context.getCharWidth())
|
||||
<< unsigned(SuitableAlign / Context.getCharWidth());
|
||||
<< unsigned(Alignment / Context.getCharWidth())
|
||||
<< unsigned(NewAlignment / Context.getCharWidth());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1880,7 +1980,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
|
|||
}
|
||||
|
||||
return new (Context)
|
||||
CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete,
|
||||
CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, PassAlignment,
|
||||
UsualArrayDeleteWantsSize, PlacementArgs, TypeIdParens,
|
||||
ArraySize, initStyle, Initializer, ResultType, AllocTypeInfo,
|
||||
Range, DirectInitRange);
|
||||
|
@ -1923,32 +2023,128 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Determine whether the given function is a non-placement
|
||||
/// deallocation function.
|
||||
static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
|
||||
if (FD->isInvalidDecl())
|
||||
static bool
|
||||
resolveAllocationOverload(Sema &S, LookupResult &R, SourceRange Range,
|
||||
SmallVectorImpl<Expr *> &Args, bool &PassAlignment,
|
||||
FunctionDecl *&Operator,
|
||||
OverloadCandidateSet *AlignedCandidates = nullptr,
|
||||
Expr *AlignArg = nullptr) {
|
||||
OverloadCandidateSet Candidates(R.getNameLoc(),
|
||||
OverloadCandidateSet::CSK_Normal);
|
||||
for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end();
|
||||
Alloc != AllocEnd; ++Alloc) {
|
||||
// Even member operator new/delete are implicitly treated as
|
||||
// static, so don't use AddMemberCandidate.
|
||||
NamedDecl *D = (*Alloc)->getUnderlyingDecl();
|
||||
|
||||
if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
|
||||
S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(),
|
||||
/*ExplicitTemplateArgs=*/nullptr, Args,
|
||||
Candidates,
|
||||
/*SuppressUserConversions=*/false);
|
||||
continue;
|
||||
}
|
||||
|
||||
FunctionDecl *Fn = cast<FunctionDecl>(D);
|
||||
S.AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates,
|
||||
/*SuppressUserConversions=*/false);
|
||||
}
|
||||
|
||||
// Do the resolution.
|
||||
OverloadCandidateSet::iterator Best;
|
||||
switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
|
||||
case OR_Success: {
|
||||
// Got one!
|
||||
FunctionDecl *FnDecl = Best->Function;
|
||||
if (S.CheckAllocationAccess(R.getNameLoc(), Range, R.getNamingClass(),
|
||||
Best->FoundDecl) == Sema::AR_inaccessible)
|
||||
return true;
|
||||
|
||||
Operator = FnDecl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD))
|
||||
return Method->isUsualDeallocationFunction();
|
||||
case OR_No_Viable_Function:
|
||||
// C++17 [expr.new]p13:
|
||||
// If no matching function is found and the allocated object type has
|
||||
// new-extended alignment, the alignment argument is removed from the
|
||||
// argument list, and overload resolution is performed again.
|
||||
if (PassAlignment) {
|
||||
PassAlignment = false;
|
||||
AlignArg = Args[1];
|
||||
Args.erase(Args.begin() + 1);
|
||||
return resolveAllocationOverload(S, R, Range, Args, PassAlignment,
|
||||
Operator, &Candidates, AlignArg);
|
||||
}
|
||||
|
||||
if (FD->getOverloadedOperator() != OO_Delete &&
|
||||
FD->getOverloadedOperator() != OO_Array_Delete)
|
||||
return false;
|
||||
// MSVC will fall back on trying to find a matching global operator new
|
||||
// if operator new[] cannot be found. Also, MSVC will leak by not
|
||||
// generating a call to operator delete or operator delete[], but we
|
||||
// will not replicate that bug.
|
||||
// FIXME: Find out how this interacts with the std::align_val_t fallback
|
||||
// once MSVC implements it.
|
||||
if (R.getLookupName().getCXXOverloadedOperator() == OO_Array_New &&
|
||||
S.Context.getLangOpts().MSVCCompat) {
|
||||
R.clear();
|
||||
R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
|
||||
S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl());
|
||||
// FIXME: This will give bad diagnostics pointing at the wrong functions.
|
||||
return resolveAllocationOverload(S, R, Range, Args, PassAlignment,
|
||||
Operator, nullptr);
|
||||
}
|
||||
|
||||
if (FD->getNumParams() == 1)
|
||||
S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call)
|
||||
<< R.getLookupName() << Range;
|
||||
|
||||
// If we have aligned candidates, only note the align_val_t candidates
|
||||
// from AlignedCandidates and the non-align_val_t candidates from
|
||||
// Candidates.
|
||||
if (AlignedCandidates) {
|
||||
auto IsAligned = [](OverloadCandidate &C) {
|
||||
return C.Function->getNumParams() > 1 &&
|
||||
C.Function->getParamDecl(1)->getType()->isAlignValT();
|
||||
};
|
||||
auto IsUnaligned = [&](OverloadCandidate &C) { return !IsAligned(C); };
|
||||
|
||||
// This was an overaligned allocation, so list the aligned candidates
|
||||
// first.
|
||||
Args.insert(Args.begin() + 1, AlignArg);
|
||||
AlignedCandidates->NoteCandidates(S, OCD_AllCandidates, Args, "",
|
||||
R.getNameLoc(), IsAligned);
|
||||
Args.erase(Args.begin() + 1);
|
||||
Candidates.NoteCandidates(S, OCD_AllCandidates, Args, "", R.getNameLoc(),
|
||||
IsUnaligned);
|
||||
} else {
|
||||
Candidates.NoteCandidates(S, OCD_AllCandidates, Args);
|
||||
}
|
||||
return true;
|
||||
|
||||
return S.getLangOpts().SizedDeallocation && FD->getNumParams() == 2 &&
|
||||
S.Context.hasSameUnqualifiedType(FD->getParamDecl(1)->getType(),
|
||||
S.Context.getSizeType());
|
||||
case OR_Ambiguous:
|
||||
S.Diag(R.getNameLoc(), diag::err_ovl_ambiguous_call)
|
||||
<< R.getLookupName() << Range;
|
||||
Candidates.NoteCandidates(S, OCD_ViableCandidates, Args);
|
||||
return true;
|
||||
|
||||
case OR_Deleted: {
|
||||
S.Diag(R.getNameLoc(), diag::err_ovl_deleted_call)
|
||||
<< Best->Function->isDeleted()
|
||||
<< R.getLookupName()
|
||||
<< S.getDeletedOrUnavailableSuffix(Best->Function)
|
||||
<< Range;
|
||||
Candidates.NoteCandidates(S, OCD_AllCandidates, Args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
llvm_unreachable("Unreachable, bad result from BestViableFunction");
|
||||
}
|
||||
|
||||
|
||||
/// FindAllocationFunctions - Finds the overloads of operator new and delete
|
||||
/// that are appropriate for the allocation.
|
||||
bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
||||
bool UseGlobal, QualType AllocType,
|
||||
bool IsArray, MultiExprArg PlaceArgs,
|
||||
bool IsArray, bool &PassAlignment,
|
||||
MultiExprArg PlaceArgs,
|
||||
FunctionDecl *&OperatorNew,
|
||||
FunctionDecl *&OperatorDelete) {
|
||||
// --- Choosing an allocation function ---
|
||||
|
@ -1960,16 +2156,29 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
// 3) The first argument is always size_t. Append the arguments from the
|
||||
// placement form.
|
||||
|
||||
SmallVector<Expr*, 8> AllocArgs(1 + PlaceArgs.size());
|
||||
// We don't care about the actual value of this argument.
|
||||
SmallVector<Expr*, 8> AllocArgs;
|
||||
AllocArgs.reserve((PassAlignment ? 2 : 1) + PlaceArgs.size());
|
||||
|
||||
// We don't care about the actual value of these arguments.
|
||||
// FIXME: Should the Sema create the expression and embed it in the syntax
|
||||
// tree? Or should the consumer just recalculate the value?
|
||||
// FIXME: Using a dummy value will interact poorly with attribute enable_if.
|
||||
IntegerLiteral Size(Context, llvm::APInt::getNullValue(
|
||||
Context.getTargetInfo().getPointerWidth(0)),
|
||||
Context.getSizeType(),
|
||||
SourceLocation());
|
||||
AllocArgs[0] = &Size;
|
||||
std::copy(PlaceArgs.begin(), PlaceArgs.end(), AllocArgs.begin() + 1);
|
||||
AllocArgs.push_back(&Size);
|
||||
|
||||
QualType AlignValT = Context.VoidTy;
|
||||
if (PassAlignment) {
|
||||
DeclareGlobalNewDelete();
|
||||
AlignValT = Context.getTypeDeclType(getStdAlignValT());
|
||||
}
|
||||
CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
|
||||
if (PassAlignment)
|
||||
AllocArgs.push_back(&Align);
|
||||
|
||||
AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end());
|
||||
|
||||
// C++ [expr.new]p8:
|
||||
// If the allocated type is a non-array type, the allocation
|
||||
|
@ -1978,50 +2187,57 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
// type, the allocation function's name is operator new[] and the
|
||||
// deallocation function's name is operator delete[].
|
||||
DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
|
||||
IsArray ? OO_Array_New : OO_New);
|
||||
DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName(
|
||||
IsArray ? OO_Array_Delete : OO_Delete);
|
||||
IsArray ? OO_Array_New : OO_New);
|
||||
|
||||
QualType AllocElemType = Context.getBaseElementType(AllocType);
|
||||
|
||||
if (AllocElemType->isRecordType() && !UseGlobal) {
|
||||
CXXRecordDecl *Record
|
||||
= cast<CXXRecordDecl>(AllocElemType->getAs<RecordType>()->getDecl());
|
||||
if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, Record,
|
||||
/*AllowMissing=*/true, OperatorNew))
|
||||
// Find the allocation function.
|
||||
{
|
||||
LookupResult R(*this, NewName, StartLoc, LookupOrdinaryName);
|
||||
|
||||
// C++1z [expr.new]p9:
|
||||
// If the new-expression begins with a unary :: operator, the allocation
|
||||
// function's name is looked up in the global scope. Otherwise, if the
|
||||
// allocated type is a class type T or array thereof, the allocation
|
||||
// function's name is looked up in the scope of T.
|
||||
if (AllocElemType->isRecordType() && !UseGlobal)
|
||||
LookupQualifiedName(R, AllocElemType->getAsCXXRecordDecl());
|
||||
|
||||
// We can see ambiguity here if the allocation function is found in
|
||||
// multiple base classes.
|
||||
if (R.isAmbiguous())
|
||||
return true;
|
||||
|
||||
// If this lookup fails to find the name, or if the allocated type is not
|
||||
// a class type, the allocation function's name is looked up in the
|
||||
// global scope.
|
||||
if (R.empty())
|
||||
LookupQualifiedName(R, Context.getTranslationUnitDecl());
|
||||
|
||||
assert(!R.empty() && "implicitly declared allocation functions not found");
|
||||
assert(!R.isAmbiguous() && "global allocation functions are ambiguous");
|
||||
|
||||
// We do our own custom access checks below.
|
||||
R.suppressDiagnostics();
|
||||
|
||||
if (resolveAllocationOverload(*this, R, Range, AllocArgs, PassAlignment,
|
||||
OperatorNew))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!OperatorNew) {
|
||||
// Didn't find a member overload. Look for a global one.
|
||||
DeclareGlobalNewDelete();
|
||||
DeclContext *TUDecl = Context.getTranslationUnitDecl();
|
||||
bool FallbackEnabled = IsArray && Context.getLangOpts().MSVCCompat;
|
||||
if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, TUDecl,
|
||||
/*AllowMissing=*/FallbackEnabled, OperatorNew,
|
||||
/*Diagnose=*/!FallbackEnabled)) {
|
||||
if (!FallbackEnabled)
|
||||
return true;
|
||||
|
||||
// MSVC will fall back on trying to find a matching global operator new
|
||||
// if operator new[] cannot be found. Also, MSVC will leak by not
|
||||
// generating a call to operator delete or operator delete[], but we
|
||||
// will not replicate that bug.
|
||||
NewName = Context.DeclarationNames.getCXXOperatorName(OO_New);
|
||||
DeleteName = Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, TUDecl,
|
||||
/*AllowMissing=*/false, OperatorNew))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need an operator delete if we're running under
|
||||
// -fno-exceptions.
|
||||
// We don't need an operator delete if we're running under -fno-exceptions.
|
||||
if (!getLangOpts().Exceptions) {
|
||||
OperatorDelete = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note, the name of OperatorNew might have been changed from array to
|
||||
// non-array by resolveAllocationOverload.
|
||||
DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName(
|
||||
OperatorNew->getDeclName().getCXXOverloadedOperator() == OO_Array_New
|
||||
? OO_Array_Delete
|
||||
: OO_Delete);
|
||||
|
||||
// C++ [expr.new]p19:
|
||||
//
|
||||
// If the new-expression begins with a unary :: operator, the
|
||||
|
@ -2040,6 +2256,7 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
if (FoundDelete.isAmbiguous())
|
||||
return true; // FIXME: clean up expressions?
|
||||
|
||||
bool FoundGlobalDelete = FoundDelete.empty();
|
||||
if (FoundDelete.empty()) {
|
||||
DeclareGlobalNewDelete();
|
||||
LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
|
||||
|
@ -2054,7 +2271,16 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
// we had explicit placement arguments. This matters for things like
|
||||
// struct A { void *operator new(size_t, int = 0); ... };
|
||||
// A *a = new A()
|
||||
bool isPlacementNew = (!PlaceArgs.empty() || OperatorNew->param_size() != 1);
|
||||
//
|
||||
// We don't have any definition for what a "placement allocation function"
|
||||
// is, but we assume it's any allocation function whose
|
||||
// parameter-declaration-clause is anything other than (size_t).
|
||||
//
|
||||
// FIXME: Should (size_t, std::align_val_t) also be considered non-placement?
|
||||
// This affects whether an exception from the constructor of an overaligned
|
||||
// type uses the sized or non-sized form of aligned operator delete.
|
||||
bool isPlacementNew = !PlaceArgs.empty() || OperatorNew->param_size() != 1 ||
|
||||
OperatorNew->isVariadic();
|
||||
|
||||
if (isPlacementNew) {
|
||||
// C++ [expr.new]p20:
|
||||
|
@ -2080,7 +2306,9 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
ArgTypes.push_back(Proto->getParamType(I));
|
||||
|
||||
FunctionProtoType::ExtProtoInfo EPI;
|
||||
// FIXME: This is not part of the standard's rule.
|
||||
EPI.Variadic = Proto->isVariadic();
|
||||
EPI.ExceptionSpec.Type = EST_BasicNoexcept;
|
||||
|
||||
ExpectedFunctionType
|
||||
= Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
|
||||
|
@ -2104,35 +2332,29 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
if (Context.hasSameType(Fn->getType(), ExpectedFunctionType))
|
||||
Matches.push_back(std::make_pair(D.getPair(), Fn));
|
||||
}
|
||||
} else {
|
||||
// C++ [expr.new]p20:
|
||||
// [...] Any non-placement deallocation function matches a
|
||||
// non-placement allocation function. [...]
|
||||
for (LookupResult::iterator D = FoundDelete.begin(),
|
||||
DEnd = FoundDelete.end();
|
||||
D != DEnd; ++D) {
|
||||
if (FunctionDecl *Fn = dyn_cast<FunctionDecl>((*D)->getUnderlyingDecl()))
|
||||
if (isNonPlacementDeallocationFunction(*this, Fn))
|
||||
Matches.push_back(std::make_pair(D.getPair(), Fn));
|
||||
}
|
||||
|
||||
if (getLangOpts().CUDA)
|
||||
EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches);
|
||||
} else {
|
||||
// C++1y [expr.new]p22:
|
||||
// For a non-placement allocation function, the normal deallocation
|
||||
// function lookup is used
|
||||
// C++1y [expr.delete]p?:
|
||||
// If [...] deallocation function lookup finds both a usual deallocation
|
||||
// function with only a pointer parameter and a usual deallocation
|
||||
// function with both a pointer parameter and a size parameter, then the
|
||||
// selected deallocation function shall be the one with two parameters.
|
||||
// Otherwise, the selected deallocation function shall be the function
|
||||
// with one parameter.
|
||||
if (getLangOpts().SizedDeallocation && Matches.size() == 2) {
|
||||
if (Matches[0].second->getNumParams() == 1)
|
||||
Matches.erase(Matches.begin());
|
||||
else
|
||||
Matches.erase(Matches.begin() + 1);
|
||||
assert(Matches[0].second->getNumParams() == 2 &&
|
||||
"found an unexpected usual deallocation function");
|
||||
//
|
||||
// Per [expr.delete]p10, this lookup prefers a member operator delete
|
||||
// without a size_t argument, but prefers a non-member operator delete
|
||||
// with a size_t where possible (which it always is in this case).
|
||||
llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns;
|
||||
UsualDeallocFnInfo Selected = resolveDeallocationOverload(
|
||||
*this, FoundDelete, /*WantSize*/ FoundGlobalDelete,
|
||||
/*WantAlign*/ hasNewExtendedAlignment(*this, AllocElemType),
|
||||
&BestDeallocFns);
|
||||
if (Selected)
|
||||
Matches.push_back(std::make_pair(Selected.Found, Selected.FD));
|
||||
else {
|
||||
// If we failed to select an operator, all remaining functions are viable
|
||||
// but ambiguous.
|
||||
for (auto Fn : BestDeallocFns)
|
||||
Matches.push_back(std::make_pair(Fn.Found, Fn.FD));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2143,130 +2365,58 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
|
|||
if (Matches.size() == 1) {
|
||||
OperatorDelete = Matches[0].second;
|
||||
|
||||
// C++0x [expr.new]p20:
|
||||
// If the lookup finds the two-parameter form of a usual
|
||||
// deallocation function (3.7.4.2) and that function, considered
|
||||
// C++1z [expr.new]p23:
|
||||
// If the lookup finds a usual deallocation function (3.7.4.2)
|
||||
// with a parameter of type std::size_t and that function, considered
|
||||
// as a placement deallocation function, would have been
|
||||
// selected as a match for the allocation function, the program
|
||||
// is ill-formed.
|
||||
if (!PlaceArgs.empty() && getLangOpts().CPlusPlus11 &&
|
||||
if (getLangOpts().CPlusPlus11 && isPlacementNew &&
|
||||
isNonPlacementDeallocationFunction(*this, OperatorDelete)) {
|
||||
Diag(StartLoc, diag::err_placement_new_non_placement_delete)
|
||||
<< SourceRange(PlaceArgs.front()->getLocStart(),
|
||||
PlaceArgs.back()->getLocEnd());
|
||||
if (!OperatorDelete->isImplicit())
|
||||
Diag(OperatorDelete->getLocation(), diag::note_previous_decl)
|
||||
<< DeleteName;
|
||||
} else {
|
||||
CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(),
|
||||
Matches[0].first);
|
||||
UsualDeallocFnInfo Info(DeclAccessPair::make(OperatorDelete, AS_public));
|
||||
// Core issue, per mail to core reflector, 2016-10-09:
|
||||
// If this is a member operator delete, and there is a corresponding
|
||||
// non-sized member operator delete, this isn't /really/ a sized
|
||||
// deallocation function, it just happens to have a size_t parameter.
|
||||
bool IsSizedDelete = Info.HasSizeT;
|
||||
if (IsSizedDelete && !FoundGlobalDelete) {
|
||||
auto NonSizedDelete =
|
||||
resolveDeallocationOverload(*this, FoundDelete, /*WantSize*/false,
|
||||
/*WantAlign*/Info.HasAlignValT);
|
||||
if (NonSizedDelete && !NonSizedDelete.HasSizeT &&
|
||||
NonSizedDelete.HasAlignValT == Info.HasAlignValT)
|
||||
IsSizedDelete = false;
|
||||
}
|
||||
|
||||
if (IsSizedDelete) {
|
||||
SourceRange R = PlaceArgs.empty()
|
||||
? SourceRange()
|
||||
: SourceRange(PlaceArgs.front()->getLocStart(),
|
||||
PlaceArgs.back()->getLocEnd());
|
||||
Diag(StartLoc, diag::err_placement_new_non_placement_delete) << R;
|
||||
if (!OperatorDelete->isImplicit())
|
||||
Diag(OperatorDelete->getLocation(), diag::note_previous_decl)
|
||||
<< DeleteName;
|
||||
}
|
||||
}
|
||||
|
||||
CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(),
|
||||
Matches[0].first);
|
||||
} else if (!Matches.empty()) {
|
||||
// We found multiple suitable operators. Per [expr.new]p20, that means we
|
||||
// call no 'operator delete' function, but we should at least warn the user.
|
||||
// FIXME: Suppress this warning if the construction cannot throw.
|
||||
Diag(StartLoc, diag::warn_ambiguous_suitable_delete_function_found)
|
||||
<< DeleteName << AllocElemType;
|
||||
|
||||
for (auto &Match : Matches)
|
||||
Diag(Match.second->getLocation(),
|
||||
diag::note_member_declared_here) << DeleteName;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Find an fitting overload for the allocation function
|
||||
/// in the specified scope.
|
||||
///
|
||||
/// \param StartLoc The location of the 'new' token.
|
||||
/// \param Range The range of the placement arguments.
|
||||
/// \param Name The name of the function ('operator new' or 'operator new[]').
|
||||
/// \param Args The placement arguments specified.
|
||||
/// \param Ctx The scope in which we should search; either a class scope or the
|
||||
/// translation unit.
|
||||
/// \param AllowMissing If \c true, report an error if we can't find any
|
||||
/// allocation functions. Otherwise, succeed but don't fill in \p
|
||||
/// Operator.
|
||||
/// \param Operator Filled in with the found allocation function. Unchanged if
|
||||
/// no allocation function was found.
|
||||
/// \param Diagnose If \c true, issue errors if the allocation function is not
|
||||
/// usable.
|
||||
bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range,
|
||||
DeclarationName Name, MultiExprArg Args,
|
||||
DeclContext *Ctx,
|
||||
bool AllowMissing, FunctionDecl *&Operator,
|
||||
bool Diagnose) {
|
||||
LookupResult R(*this, Name, StartLoc, LookupOrdinaryName);
|
||||
LookupQualifiedName(R, Ctx);
|
||||
if (R.empty()) {
|
||||
if (AllowMissing || !Diagnose)
|
||||
return false;
|
||||
return Diag(StartLoc, diag::err_ovl_no_viable_function_in_call)
|
||||
<< Name << Range;
|
||||
}
|
||||
|
||||
if (R.isAmbiguous())
|
||||
return true;
|
||||
|
||||
R.suppressDiagnostics();
|
||||
|
||||
OverloadCandidateSet Candidates(StartLoc, OverloadCandidateSet::CSK_Normal);
|
||||
for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end();
|
||||
Alloc != AllocEnd; ++Alloc) {
|
||||
// Even member operator new/delete are implicitly treated as
|
||||
// static, so don't use AddMemberCandidate.
|
||||
NamedDecl *D = (*Alloc)->getUnderlyingDecl();
|
||||
|
||||
if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
|
||||
AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(),
|
||||
/*ExplicitTemplateArgs=*/nullptr,
|
||||
Args, Candidates,
|
||||
/*SuppressUserConversions=*/false);
|
||||
continue;
|
||||
}
|
||||
|
||||
FunctionDecl *Fn = cast<FunctionDecl>(D);
|
||||
AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates,
|
||||
/*SuppressUserConversions=*/false);
|
||||
}
|
||||
|
||||
// Do the resolution.
|
||||
OverloadCandidateSet::iterator Best;
|
||||
switch (Candidates.BestViableFunction(*this, StartLoc, Best)) {
|
||||
case OR_Success: {
|
||||
// Got one!
|
||||
FunctionDecl *FnDecl = Best->Function;
|
||||
if (CheckAllocationAccess(StartLoc, Range, R.getNamingClass(),
|
||||
Best->FoundDecl, Diagnose) == AR_inaccessible)
|
||||
return true;
|
||||
|
||||
Operator = FnDecl;
|
||||
return false;
|
||||
}
|
||||
|
||||
case OR_No_Viable_Function:
|
||||
if (Diagnose) {
|
||||
Diag(StartLoc, diag::err_ovl_no_viable_function_in_call)
|
||||
<< Name << Range;
|
||||
Candidates.NoteCandidates(*this, OCD_AllCandidates, Args);
|
||||
}
|
||||
return true;
|
||||
|
||||
case OR_Ambiguous:
|
||||
if (Diagnose) {
|
||||
Diag(StartLoc, diag::err_ovl_ambiguous_call)
|
||||
<< Name << Range;
|
||||
Candidates.NoteCandidates(*this, OCD_ViableCandidates, Args);
|
||||
}
|
||||
return true;
|
||||
|
||||
case OR_Deleted: {
|
||||
if (Diagnose) {
|
||||
Diag(StartLoc, diag::err_ovl_deleted_call)
|
||||
<< Best->Function->isDeleted()
|
||||
<< Name
|
||||
<< getDeletedOrUnavailableSuffix(Best->Function)
|
||||
<< Range;
|
||||
Candidates.NoteCandidates(*this, OCD_AllCandidates, Args);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
llvm_unreachable("Unreachable, bad result from BestViableFunction");
|
||||
}
|
||||
|
||||
|
||||
/// DeclareGlobalNewDelete - Declare the global forms of operator new and
|
||||
/// delete. These are:
|
||||
/// @code
|
||||
|
@ -2460,52 +2610,43 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
|
|||
|
||||
FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
|
||||
bool CanProvideSize,
|
||||
bool Overaligned,
|
||||
DeclarationName Name) {
|
||||
DeclareGlobalNewDelete();
|
||||
|
||||
LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
|
||||
LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl());
|
||||
|
||||
// C++ [expr.new]p20:
|
||||
// [...] Any non-placement deallocation function matches a
|
||||
// non-placement allocation function. [...]
|
||||
llvm::SmallVector<FunctionDecl*, 2> Matches;
|
||||
for (LookupResult::iterator D = FoundDelete.begin(),
|
||||
DEnd = FoundDelete.end();
|
||||
D != DEnd; ++D) {
|
||||
if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(*D))
|
||||
if (isNonPlacementDeallocationFunction(*this, Fn))
|
||||
Matches.push_back(Fn);
|
||||
}
|
||||
// FIXME: It's possible for this to result in ambiguity, through a
|
||||
// user-declared variadic operator delete or the enable_if attribute. We
|
||||
// should probably not consider those cases to be usual deallocation
|
||||
// functions. But for now we just make an arbitrary choice in that case.
|
||||
auto Result = resolveDeallocationOverload(*this, FoundDelete, CanProvideSize,
|
||||
Overaligned);
|
||||
assert(Result.FD && "operator delete missing from global scope?");
|
||||
return Result.FD;
|
||||
}
|
||||
|
||||
// C++1y [expr.delete]p?:
|
||||
// If the type is complete and deallocation function lookup finds both a
|
||||
// usual deallocation function with only a pointer parameter and a usual
|
||||
// deallocation function with both a pointer parameter and a size
|
||||
// parameter, then the selected deallocation function shall be the one
|
||||
// with two parameters. Otherwise, the selected deallocation function
|
||||
// shall be the function with one parameter.
|
||||
if (getLangOpts().SizedDeallocation && Matches.size() == 2) {
|
||||
unsigned NumArgs = CanProvideSize ? 2 : 1;
|
||||
if (Matches[0]->getNumParams() != NumArgs)
|
||||
Matches.erase(Matches.begin());
|
||||
else
|
||||
Matches.erase(Matches.begin() + 1);
|
||||
assert(Matches[0]->getNumParams() == NumArgs &&
|
||||
"found an unexpected usual deallocation function");
|
||||
}
|
||||
FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
|
||||
CXXRecordDecl *RD) {
|
||||
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
|
||||
if (getLangOpts().CUDA)
|
||||
EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches);
|
||||
FunctionDecl *OperatorDelete = nullptr;
|
||||
if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete))
|
||||
return nullptr;
|
||||
if (OperatorDelete)
|
||||
return OperatorDelete;
|
||||
|
||||
assert(Matches.size() == 1 &&
|
||||
"unexpectedly have multiple usual deallocation functions");
|
||||
return Matches.front();
|
||||
// If there's no class-specific operator delete, look up the global
|
||||
// non-array delete.
|
||||
return FindUsualDeallocationFunction(
|
||||
Loc, true, hasNewExtendedAlignment(*this, Context.getRecordType(RD)),
|
||||
Name);
|
||||
}
|
||||
|
||||
bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
||||
DeclarationName Name,
|
||||
FunctionDecl* &Operator, bool Diagnose) {
|
||||
FunctionDecl *&Operator, bool Diagnose) {
|
||||
LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
|
||||
// Try to find operator delete/operator delete[] in class scope.
|
||||
LookupQualifiedName(Found, RD);
|
||||
|
@ -2515,27 +2656,20 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
|||
|
||||
Found.suppressDiagnostics();
|
||||
|
||||
SmallVector<DeclAccessPair,4> Matches;
|
||||
for (LookupResult::iterator F = Found.begin(), FEnd = Found.end();
|
||||
F != FEnd; ++F) {
|
||||
NamedDecl *ND = (*F)->getUnderlyingDecl();
|
||||
bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD));
|
||||
|
||||
// Ignore template operator delete members from the check for a usual
|
||||
// deallocation function.
|
||||
if (isa<FunctionTemplateDecl>(ND))
|
||||
continue;
|
||||
// C++17 [expr.delete]p10:
|
||||
// If the deallocation functions have class scope, the one without a
|
||||
// parameter of type std::size_t is selected.
|
||||
llvm::SmallVector<UsualDeallocFnInfo, 4> Matches;
|
||||
resolveDeallocationOverload(*this, Found, /*WantSize*/ false,
|
||||
/*WantAlign*/ Overaligned, &Matches);
|
||||
|
||||
if (cast<CXXMethodDecl>(ND)->isUsualDeallocationFunction())
|
||||
Matches.push_back(F.getPair());
|
||||
}
|
||||
|
||||
if (getLangOpts().CUDA)
|
||||
EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches);
|
||||
|
||||
// There's exactly one suitable operator; pick it.
|
||||
// If we could find an overload, use it.
|
||||
if (Matches.size() == 1) {
|
||||
Operator = cast<CXXMethodDecl>(Matches[0]->getUnderlyingDecl());
|
||||
Operator = cast<CXXMethodDecl>(Matches[0].FD);
|
||||
|
||||
// FIXME: DiagnoseUseOfDecl?
|
||||
if (Operator->isDeleted()) {
|
||||
if (Diagnose) {
|
||||
Diag(StartLoc, diag::err_deleted_function_use);
|
||||
|
@ -2545,21 +2679,21 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
|||
}
|
||||
|
||||
if (CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(),
|
||||
Matches[0], Diagnose) == AR_inaccessible)
|
||||
Matches[0].Found, Diagnose) == AR_inaccessible)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We found multiple suitable operators; complain about the ambiguity.
|
||||
} else if (!Matches.empty()) {
|
||||
// We found multiple suitable operators; complain about the ambiguity.
|
||||
// FIXME: The standard doesn't say to do this; it appears that the intent
|
||||
// is that this should never happen.
|
||||
if (!Matches.empty()) {
|
||||
if (Diagnose) {
|
||||
Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found)
|
||||
<< Name << RD;
|
||||
|
||||
for (SmallVectorImpl<DeclAccessPair>::iterator
|
||||
F = Matches.begin(), FEnd = Matches.end(); F != FEnd; ++F)
|
||||
Diag((*F)->getUnderlyingDecl()->getLocation(),
|
||||
diag::note_member_declared_here) << Name;
|
||||
for (auto &Match : Matches)
|
||||
Diag(Match.FD->getLocation(), diag::note_member_declared_here) << Name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2571,9 +2705,8 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
|||
Diag(StartLoc, diag::err_no_suitable_delete_member_function_found)
|
||||
<< Name << RD;
|
||||
|
||||
for (LookupResult::iterator F = Found.begin(), FEnd = Found.end();
|
||||
F != FEnd; ++F)
|
||||
Diag((*F)->getUnderlyingDecl()->getLocation(),
|
||||
for (NamedDecl *D : Found)
|
||||
Diag(D->getUnderlyingDecl()->getLocation(),
|
||||
diag::note_member_declared_here) << Name;
|
||||
}
|
||||
return true;
|
||||
|
@ -2984,7 +3117,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
|
|||
// Otherwise, the usual operator delete[] should be the
|
||||
// function we just found.
|
||||
else if (OperatorDelete && isa<CXXMethodDecl>(OperatorDelete))
|
||||
UsualArrayDeleteWantsSize = (OperatorDelete->getNumParams() == 2);
|
||||
UsualArrayDeleteWantsSize =
|
||||
UsualDeallocFnInfo(
|
||||
DeclAccessPair::make(OperatorDelete, AS_public))
|
||||
.HasSizeT;
|
||||
}
|
||||
|
||||
if (!PointeeRD->hasIrrelevantDestructor())
|
||||
|
@ -3001,13 +3137,17 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
|
|||
SourceLocation());
|
||||
}
|
||||
|
||||
if (!OperatorDelete)
|
||||
if (!OperatorDelete) {
|
||||
bool IsComplete = isCompleteType(StartLoc, Pointee);
|
||||
bool CanProvideSize =
|
||||
IsComplete && (!ArrayForm || UsualArrayDeleteWantsSize ||
|
||||
Pointee.isDestructedType());
|
||||
bool Overaligned = hasNewExtendedAlignment(*this, Pointee);
|
||||
|
||||
// Look for a global declaration.
|
||||
OperatorDelete = FindUsualDeallocationFunction(
|
||||
StartLoc, isCompleteType(StartLoc, Pointee) &&
|
||||
(!ArrayForm || UsualArrayDeleteWantsSize ||
|
||||
Pointee.isDestructedType()),
|
||||
DeleteName);
|
||||
OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize,
|
||||
Overaligned, DeleteName);
|
||||
}
|
||||
|
||||
MarkFunctionReferenced(StartLoc, OperatorDelete);
|
||||
|
||||
|
|
|
@ -10142,16 +10142,17 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
|
|||
/// PrintOverloadCandidates - When overload resolution fails, prints
|
||||
/// diagnostic messages containing the candidates in the candidate
|
||||
/// set.
|
||||
void OverloadCandidateSet::NoteCandidates(Sema &S,
|
||||
OverloadCandidateDisplayKind OCD,
|
||||
ArrayRef<Expr *> Args,
|
||||
StringRef Opc,
|
||||
SourceLocation OpLoc) {
|
||||
void OverloadCandidateSet::NoteCandidates(
|
||||
Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args,
|
||||
StringRef Opc, SourceLocation OpLoc,
|
||||
llvm::function_ref<bool(OverloadCandidate &)> Filter) {
|
||||
// Sort the candidates by viability and position. Sorting directly would
|
||||
// be prohibitive, so we make a set of pointers and sort those.
|
||||
SmallVector<OverloadCandidate*, 32> Cands;
|
||||
if (OCD == OCD_AllCandidates) Cands.reserve(size());
|
||||
for (iterator Cand = begin(), LastCand = end(); Cand != LastCand; ++Cand) {
|
||||
if (!Filter(*Cand))
|
||||
continue;
|
||||
if (Cand->Viable)
|
||||
Cands.push_back(Cand);
|
||||
else if (OCD == OCD_AllCandidates) {
|
||||
|
|
|
@ -1410,6 +1410,7 @@ void ASTStmtReader::VisitCXXNewExpr(CXXNewExpr *E) {
|
|||
VisitExpr(E);
|
||||
E->GlobalNew = Record[Idx++];
|
||||
bool isArray = Record[Idx++];
|
||||
E->PassAlignment = Record[Idx++];
|
||||
E->UsualArrayDeleteWantsSize = Record[Idx++];
|
||||
unsigned NumPlacementArgs = Record[Idx++];
|
||||
E->StoredInitializationStyle = Record[Idx++];
|
||||
|
|
|
@ -1392,6 +1392,7 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
|
|||
VisitExpr(E);
|
||||
Record.push_back(E->isGlobalNew());
|
||||
Record.push_back(E->isArray());
|
||||
Record.push_back(E->passAlignment());
|
||||
Record.push_back(E->doesUsualArrayDeleteWantSize());
|
||||
Record.push_back(E->getNumPlacementArgs());
|
||||
Record.push_back(E->StoredInitializationStyle);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -fsized-deallocation -fexceptions -verify %s
|
||||
|
||||
using size_t = decltype(sizeof(0));
|
||||
|
||||
namespace std { enum class align_val_t : size_t {}; }
|
||||
|
||||
// p2 says "A template instance is never a usual deallocation function,
|
||||
// regardless of its signature." We (and every other implementation) assume
|
||||
// this means "A function template specialization [...]"
|
||||
template<typename...Ts> struct A {
|
||||
void *operator new(size_t);
|
||||
void operator delete(void*, Ts...) = delete; // expected-note 4{{deleted}}
|
||||
};
|
||||
|
||||
auto *a1 = new A<>; // expected-error {{deleted}}
|
||||
auto *a2 = new A<size_t>; // expected-error {{deleted}}
|
||||
auto *a3 = new A<std::align_val_t>; // expected-error {{deleted}}
|
||||
auto *a4 = new A<size_t, std::align_val_t>; // expected-error {{deleted}}
|
||||
auto *a5 = new A<std::align_val_t, size_t>; // ok, not usual
|
|
@ -0,0 +1,25 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -verify %s
|
||||
|
||||
using size_t = decltype(sizeof(0));
|
||||
namespace std { enum class align_val_t : size_t {}; }
|
||||
|
||||
// Aligned version is preferred over unaligned version,
|
||||
// unsized version is preferred over sized version.
|
||||
template<unsigned Align>
|
||||
struct alignas(Align) A {
|
||||
void operator delete(void*);
|
||||
void operator delete(void*, std::align_val_t) = delete; // expected-note {{here}}
|
||||
|
||||
void operator delete(void*, size_t) = delete;
|
||||
void operator delete(void*, size_t, std::align_val_t) = delete;
|
||||
};
|
||||
void f(A<__STDCPP_DEFAULT_NEW_ALIGNMENT__> *p) { delete p; }
|
||||
void f(A<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2> *p) { delete p; } // expected-error {{deleted}}
|
||||
|
||||
template<unsigned Align>
|
||||
struct alignas(Align) B {
|
||||
void operator delete(void*, size_t);
|
||||
void operator delete(void*, size_t, std::align_val_t) = delete; // expected-note {{here}}
|
||||
};
|
||||
void f(B<__STDCPP_DEFAULT_NEW_ALIGNMENT__> *p) { delete p; }
|
||||
void f(B<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2> *p) { delete p; } // expected-error {{deleted}}
|
|
@ -0,0 +1,69 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -fsized-deallocation -fexceptions %s -verify
|
||||
|
||||
using size_t = decltype(sizeof(0));
|
||||
namespace std { enum class align_val_t : size_t {}; }
|
||||
|
||||
struct Arg {} arg;
|
||||
|
||||
// If the type is aligned, first try with an alignment argument and then
|
||||
// without. If not, never consider supplying an alignment.
|
||||
|
||||
template<unsigned Align, typename ...Ts>
|
||||
struct alignas(Align) Unaligned {
|
||||
void *operator new(size_t, Ts...) = delete; // expected-note 4{{deleted}}
|
||||
};
|
||||
auto *ua = new Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{deleted}}
|
||||
auto *ub = new Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}}
|
||||
auto *uap = new (arg) Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{deleted}}
|
||||
auto *ubp = new (arg) Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}}
|
||||
|
||||
template<unsigned Align, typename ...Ts>
|
||||
struct alignas(Align) Aligned {
|
||||
void *operator new(size_t, std::align_val_t, Ts...) = delete; // expected-note 2{{deleted}} expected-note 2{{not viable}}
|
||||
};
|
||||
auto *aa = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{no matching}}
|
||||
auto *ab = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}}
|
||||
auto *aap = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{no matching}}
|
||||
auto *abp = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}}
|
||||
|
||||
// If both are available, we prefer the aligned version for an overaligned
|
||||
// type, and only use the unaligned version for a non-overaligned type.
|
||||
|
||||
template<unsigned Align, typename ...Ts>
|
||||
struct alignas(Align) Both1 {
|
||||
void *operator new(size_t, Ts...); // expected-note 2{{not viable}}
|
||||
void *operator new(size_t, std::align_val_t, Ts...) = delete; // expected-note 2{{deleted}}
|
||||
};
|
||||
template<unsigned Align, typename ...Ts>
|
||||
struct alignas(Align) Both2 {
|
||||
void *operator new(size_t, Ts...) = delete; // expected-note 2{{deleted}}
|
||||
void *operator new(size_t, std::align_val_t, Ts...); // expected-note 2{{not viable}}
|
||||
};
|
||||
auto *b1a = new Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__>;
|
||||
auto *b1b = new Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}}
|
||||
auto *b2a = new Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{deleted}}
|
||||
auto *b2b = new Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>;
|
||||
auto *b1ap = new (arg) Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>;
|
||||
auto *b1bp = new (arg) Both1<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}}
|
||||
auto *b2ap = new (arg) Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{deleted}}
|
||||
auto *b2bp = new (arg) Both2<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>;
|
||||
|
||||
// Note that the aligned form can select a function with a parameter different
|
||||
// from std::align_val_t.
|
||||
|
||||
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) WeirdAlignedAlloc1 {
|
||||
void *operator new(size_t, ...) = delete; // expected-note 2{{deleted}}
|
||||
};
|
||||
auto *waa1 = new WeirdAlignedAlloc1; // expected-error {{deleted}}
|
||||
auto *waa1p = new (arg) WeirdAlignedAlloc1; // expected-error {{deleted}}
|
||||
|
||||
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) WeirdAlignedAlloc2 {
|
||||
template<typename ...T>
|
||||
void *operator new(size_t, T...) {
|
||||
using U = void(T...); // expected-note 2{{previous}}
|
||||
using U = void; // expected-error {{different types ('void' vs 'void (std::align_val_t)')}} \
|
||||
expected-error {{different types ('void' vs 'void (std::align_val_t, Arg)')}}
|
||||
}
|
||||
};
|
||||
auto *waa2 = new WeirdAlignedAlloc2; // expected-note {{instantiation of}}
|
||||
auto *waa2p = new (arg) WeirdAlignedAlloc2; // expected-note {{instantiation of}}
|
|
@ -1,6 +1,10 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fexceptions %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -fexceptions %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z -fexceptions %s
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
namespace std { enum class align_val_t : size_t {}; }
|
||||
|
||||
struct S {
|
||||
// Placement allocation function:
|
||||
static void* operator new(size_t, size_t);
|
||||
|
@ -9,5 +13,56 @@ struct S {
|
|||
};
|
||||
|
||||
void testS() {
|
||||
S* p = new (0) S; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}}
|
||||
S* p = new (0) S; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}}
|
||||
}
|
||||
|
||||
struct T {
|
||||
// Placement allocation function:
|
||||
static void* operator new(size_t, size_t);
|
||||
// Usual (non-placement) deallocation function:
|
||||
static void operator delete(void*);
|
||||
// Placement deallocation function:
|
||||
static void operator delete(void*, size_t);
|
||||
};
|
||||
|
||||
void testT() {
|
||||
T* p = new (0) T; // ok
|
||||
}
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
struct U {
|
||||
// Placement allocation function:
|
||||
static void* operator new(size_t, size_t, std::align_val_t);
|
||||
// Placement deallocation function:
|
||||
static void operator delete(void*, size_t, std::align_val_t); // expected-note{{declared here}}
|
||||
};
|
||||
|
||||
void testU() {
|
||||
U* p = new (0, std::align_val_t(0)) U; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}}
|
||||
}
|
||||
|
||||
struct V {
|
||||
// Placement allocation function:
|
||||
static void* operator new(size_t, size_t, std::align_val_t);
|
||||
// Usual (non-placement) deallocation function:
|
||||
static void operator delete(void*, std::align_val_t);
|
||||
// Placement deallocation function:
|
||||
static void operator delete(void*, size_t, std::align_val_t);
|
||||
};
|
||||
|
||||
void testV() {
|
||||
V* p = new (0, std::align_val_t(0)) V;
|
||||
}
|
||||
|
||||
struct W {
|
||||
// Placement allocation function:
|
||||
static void* operator new(size_t, size_t, std::align_val_t);
|
||||
// Usual (non-placement) deallocation functions:
|
||||
static void operator delete(void*);
|
||||
static void operator delete(void*, size_t, std::align_val_t); // expected-note {{declared here}}
|
||||
};
|
||||
|
||||
void testW() {
|
||||
W* p = new (0, std::align_val_t(0)) W; // expected-error{{'new' expression with placement arguments refers to non-placement 'operator delete'}}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -31,13 +31,13 @@ namespace test0 {
|
|||
namespace test1 {
|
||||
class A {
|
||||
public:
|
||||
static void operator delete(void *p) {}; // expected-note {{member 'operator delete' declared here}}
|
||||
static void operator delete(void *p) {};
|
||||
virtual ~A();
|
||||
};
|
||||
|
||||
class B : protected A {
|
||||
public:
|
||||
static void operator delete(void *, size_t) {}; // expected-note {{member 'operator delete' declared here}}
|
||||
static void operator delete(void *, size_t) {};
|
||||
~B();
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,20 @@ namespace test1 {
|
|||
~C();
|
||||
};
|
||||
|
||||
C::~C() {} // expected-error {{multiple suitable 'operator delete' functions in 'C'}}
|
||||
// We assume that the intent is to treat C::operator delete(void*, size_t) as
|
||||
// /not/ being a usual deallocation function, as it would be if it were
|
||||
// declared with in C directly.
|
||||
C::~C() {}
|
||||
|
||||
struct D {
|
||||
void operator delete(void*); // expected-note {{member 'operator delete' declared here}}
|
||||
void operator delete(void*, ...); // expected-note {{member 'operator delete' declared here}}
|
||||
virtual ~D();
|
||||
};
|
||||
// FIXME: The standard doesn't say this is ill-formed, but presumably either
|
||||
// it should be or the variadic operator delete should not be a usual
|
||||
// deallocation function.
|
||||
D::~D() {} // expected-error {{multiple suitable 'operator delete' functions in 'D'}}
|
||||
}
|
||||
|
||||
// ...at the point of definition of a virtual destructor...
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
// Check that delete exprs call aligned (de)allocation functions if
|
||||
// -faligned-allocation is passed in both C++11 and C++14.
|
||||
// RUN: %clang_cc1 -std=c++11 -fexceptions -fsized-deallocation -faligned-allocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -std=c++14 -fexceptions -fsized-deallocation -faligned-allocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -std=c++1z -fexceptions -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s
|
||||
|
||||
// Check that we don't used aligned (de)allocation without -faligned-allocation or C++1z.
|
||||
// RUN: %clang_cc1 -std=c++14 -DUNALIGNED -fexceptions %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNALIGNED
|
||||
// RUN: %clang_cc1 -std=c++1z -DUNALIGNED -fexceptions -fno-aligned-allocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNALIGNED
|
||||
|
||||
// CHECK-UNALIGNED-NOT: _Znwm_St11align_val_t
|
||||
// CHECK-UNALIGNED-NOT: _Znam_St11align_val_t
|
||||
// CHECK-UNALIGNED-NOT: _ZdlPv_St11align_val_t
|
||||
// CHECK-UNALIGNED-NOT: _ZdaPv_St11align_val_t
|
||||
// CHECK-UNALIGNED-NOT: _ZdlPvm_St11align_val_t
|
||||
// CHECK-UNALIGNED-NOT: _ZdaPvm_St11align_val_t
|
||||
|
||||
typedef decltype(sizeof(0)) size_t;
|
||||
namespace std { enum class align_val_t : size_t {}; }
|
||||
|
||||
#define OVERALIGNED alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2)
|
||||
|
||||
// Global new and delete.
|
||||
// ======================
|
||||
struct OVERALIGNED A { A(); int n[128]; };
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2a0v()
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZnwmSt11align_val_t(i64 512, i64 32)
|
||||
// CHECK: call void @_ZdlPvSt11align_val_t(i8* %[[ALLOC]], i64 32)
|
||||
void *a0() { return new A; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2a1l(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZnamSt11align_val_t(i64 %{{.*}}, i64 32)
|
||||
// No array cookie.
|
||||
// CHECK-NOT: store
|
||||
// CHECK: invoke void @_ZN1AC1Ev(
|
||||
// CHECK: call void @_ZdaPvSt11align_val_t(i8* %[[ALLOC]], i64 32)
|
||||
void *a1(long n) { return new A[n]; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2a2P1A(
|
||||
// CHECK: call void @_ZdlPvmSt11align_val_t(i8* %{{.*}}, i64 512, i64 32) #9
|
||||
void a2(A *p) { delete p; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2a3P1A(
|
||||
// CHECK: call void @_ZdaPvSt11align_val_t(i8* %{{.*}}, i64 32) #9
|
||||
void a3(A *p) { delete[] p; }
|
||||
|
||||
|
||||
// Class-specific usual new and delete.
|
||||
// ====================================
|
||||
struct OVERALIGNED B {
|
||||
B();
|
||||
// These are just a distraction. We should ignore them.
|
||||
void *operator new(size_t);
|
||||
void operator delete(void*, size_t);
|
||||
void operator delete[](void*, size_t);
|
||||
|
||||
void *operator new(size_t, std::align_val_t);
|
||||
void operator delete(void*, std::align_val_t);
|
||||
void operator delete[](void*, std::align_val_t);
|
||||
|
||||
int n[128];
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2b0v()
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1BnwEmSt11align_val_t(i64 512, i64 32)
|
||||
// CHECK: call void @_ZN1BdlEPvSt11align_val_t(i8* %[[ALLOC]], i64 32)
|
||||
void *b0() { return new B; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2b1l(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZnamSt11align_val_t(i64 %{{.*}}, i64 32)
|
||||
// No array cookie.
|
||||
// CHECK-NOT: store
|
||||
// CHECK: invoke void @_ZN1BC1Ev(
|
||||
// CHECK: call void @_ZN1BdaEPvSt11align_val_t(i8* %[[ALLOC]], i64 32)
|
||||
void *b1(long n) { return new B[n]; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2b2P1B(
|
||||
// CHECK: call void @_ZN1BdlEPvSt11align_val_t(i8* %{{.*}}, i64 32)
|
||||
void b2(B *p) { delete p; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2b3P1B(
|
||||
// CHECK: call void @_ZN1BdaEPvSt11align_val_t(i8* %{{.*}}, i64 32)
|
||||
void b3(B *p) { delete[] p; }
|
||||
|
||||
struct OVERALIGNED C {
|
||||
C();
|
||||
void *operator new[](size_t, std::align_val_t);
|
||||
void operator delete[](void*, size_t, std::align_val_t);
|
||||
|
||||
// It doesn't matter that we have an unaligned operator delete[] that doesn't
|
||||
// want the size. What matters is that the aligned one does.
|
||||
void operator delete[](void*);
|
||||
};
|
||||
|
||||
// This one has an array cookie.
|
||||
// CHECK-LABEL: define {{.*}} @_Z2b4l(
|
||||
// CHECK: call {{.*}} @llvm.umul.with.overflow{{.*}}i64 32
|
||||
// CHECK: call {{.*}} @llvm.uadd.with.overflow{{.*}}i64 32
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1CnaEmSt11align_val_t(i64 %{{.*}}, i64 32)
|
||||
// CHECK: store
|
||||
// CHECK: call void @_ZN1CC1Ev(
|
||||
//
|
||||
// Note, we're still calling a placement allocation function, and there is no
|
||||
// matching placement operator delete. =(
|
||||
// FIXME: This seems broken.
|
||||
// CHECK-NOT: call void @_ZN1CdaEPvmSt11align_val_t(
|
||||
#ifndef UNALIGNED
|
||||
void *b4(long n) { return new C[n]; }
|
||||
#endif
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2b5P1C(
|
||||
// CHECK: mul i64{{.*}} 32
|
||||
// CHECK: add i64{{.*}} 32
|
||||
// CHECK: call void @_ZN1CdaEPvmSt11align_val_t(
|
||||
void b5(C *p) { delete[] p; }
|
||||
|
||||
|
||||
// Global placement new.
|
||||
// =====================
|
||||
|
||||
struct Q { int n; } q;
|
||||
void *operator new(size_t, Q);
|
||||
void *operator new(size_t, std::align_val_t, Q);
|
||||
void operator delete(void*, Q);
|
||||
void operator delete(void*, std::align_val_t, Q);
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2c0v(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZnwmSt11align_val_t1Q(i64 512, i64 32, i32 %
|
||||
// CHECK: call void @_ZdlPvSt11align_val_t1Q(i8* %[[ALLOC]], i64 32, i32 %
|
||||
void *c0() { return new (q) A; }
|
||||
|
||||
|
||||
// Class-specific placement new.
|
||||
// =============================
|
||||
|
||||
struct OVERALIGNED D {
|
||||
D();
|
||||
void *operator new(size_t, Q);
|
||||
void *operator new(size_t, std::align_val_t, Q);
|
||||
void operator delete(void*, Q);
|
||||
void operator delete(void*, std::align_val_t, Q);
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2d0v(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1DnwEmSt11align_val_t1Q(i64 32, i64 32, i32 %
|
||||
// CHECK: call void @_ZN1DdlEPvSt11align_val_t1Q(i8* %[[ALLOC]], i64 32, i32 %
|
||||
void *d0() { return new (q) D; }
|
||||
|
||||
|
||||
// Calling aligned new with placement syntax.
|
||||
// ==========================================
|
||||
|
||||
#ifndef UNALIGNED
|
||||
// CHECK-LABEL: define {{.*}} @_Z2e0v(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZnwmSt11align_val_t(i64 512, i64 5)
|
||||
// CHECK: call void @_ZdlPvSt11align_val_t(i8* %[[ALLOC]], i64 5)
|
||||
void *e0() { return new (std::align_val_t(5)) A; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2e1v(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* @_ZN1BnwEmSt11align_val_t(i64 512, i64 5)
|
||||
// CHECK: call void @_ZN1BdlEPvSt11align_val_t(i8* %[[ALLOC]], i64 5)
|
||||
void *e1() { return new (std::align_val_t(5)) B; }
|
||||
#endif
|
||||
|
||||
// Variadic placement/non-placement allocation functions.
|
||||
// ======================================================
|
||||
|
||||
struct OVERALIGNED F {
|
||||
F();
|
||||
void *operator new(size_t, ...);
|
||||
void operator delete(void*, ...);
|
||||
int n[128];
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2f0v(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* (i64, ...) @_ZN1FnwEmz(i64 512, i64 32)
|
||||
// Non-placement allocation function, uses normal deallocation lookup which
|
||||
// cares about whether a parameter has type std::align_val_t.
|
||||
// CHECK: call void (i8*, ...) @_ZN1FdlEPvz(i8* %[[ALLOC]])
|
||||
void *f0() { return new F; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2f1v(
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* (i64, ...) @_ZN1FnwEmz(i64 512, i64 32, i32 %
|
||||
// Placement allocation function, uses placement deallocation matching, which
|
||||
// passes same arguments and therefore includes alignment.
|
||||
// CHECK: call void (i8*, ...) @_ZN1FdlEPvz(i8* %[[ALLOC]], i64 32, i32 %
|
||||
void *f1() { return new (q) F; }
|
||||
|
||||
struct OVERALIGNED G {
|
||||
G();
|
||||
void *operator new(size_t, std::align_val_t, ...);
|
||||
void operator delete(void*, std::align_val_t, ...);
|
||||
int n[128];
|
||||
};
|
||||
#ifndef UNALIGNED
|
||||
// CHECK-LABEL: define {{.*}} @_Z2g0v
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* (i64, i64, ...) @_ZN1GnwEmSt11align_val_tz(i64 512, i64 32)
|
||||
// CHECK: call void (i8*, i64, ...) @_ZN1GdlEPvSt11align_val_tz(i8* %[[ALLOC]], i64 32)
|
||||
void *g0() { return new G; }
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z2g1v
|
||||
// CHECK: %[[ALLOC:.*]] = call i8* (i64, i64, ...) @_ZN1GnwEmSt11align_val_tz(i64 512, i64 32, i32 %
|
||||
// CHECK: call void (i8*, i64, ...) @_ZN1GdlEPvSt11align_val_tz(i8* %[[ALLOC]], i64 32, i32 %
|
||||
void *g1() { return new (q) G; }
|
||||
#endif
|
Loading…
Reference in New Issue