forked from OSchip/llvm-project
[Builtins] Overload __builtin_operator_new/delete to allow forwarding to usual allocation/deallocation functions.
Summary: Libc++'s default allocator uses `__builtin_operator_new` and `__builtin_operator_delete` in order to allow the calls to new/delete to be ellided. However, libc++ now needs to support over-aligned types in the default allocator. In order to support this without disabling the existing optimization Clang needs to support calling the aligned new overloads from the builtins. See llvm.org/PR22634 for more information about the libc++ bug. This patch changes `__builtin_operator_new`/`__builtin_operator_delete` to call any usual `operator new`/`operator delete` function. It does this by performing overload resolution with the arguments passed to the builtin to determine which allocation function to call. If the selected function is not a usual allocation function a diagnostic is issued. One open issue is if the `align_val_t` overloads should be considered "usual" when `LangOpts::AlignedAllocation` is disabled. In order to allow libc++ to detect this new behavior the value for `__has_builtin(__builtin_operator_new)` has been updated to `201802`. Reviewers: rsmith, majnemer, aaron.ballman, erik.pilkington, bogner, ahatanak Reviewed By: rsmith Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D43047 llvm-svn: 328134
This commit is contained in:
parent
b17fff79f0
commit
fa752f23cc
|
@ -1371,8 +1371,8 @@ BUILTIN(__builtin_smulll_overflow, "bSLLiCSLLiCSLLi*", "n")
|
|||
|
||||
// Clang builtins (not available in GCC).
|
||||
BUILTIN(__builtin_addressof, "v*v&", "nct")
|
||||
BUILTIN(__builtin_operator_new, "v*z", "c")
|
||||
BUILTIN(__builtin_operator_delete, "vv*", "n")
|
||||
BUILTIN(__builtin_operator_new, "v*z", "tc")
|
||||
BUILTIN(__builtin_operator_delete, "vv*", "tn")
|
||||
BUILTIN(__builtin_char_memchr, "c*cC*iz", "n")
|
||||
|
||||
// Safestack builtins
|
||||
|
|
|
@ -7627,6 +7627,11 @@ def err_destroying_operator_delete_not_usual : Error<
|
|||
"alignment parameter">;
|
||||
def note_implicit_delete_this_in_destructor_here : Note<
|
||||
"while checking implicit 'delete this' for virtual destructor">;
|
||||
def err_builtin_operator_new_delete_not_usual : Error<
|
||||
"call to '%select{__builtin_operator_new|__builtin_operator_delete}0' "
|
||||
"selects non-usual %select{allocation|deallocation}0 function">;
|
||||
def note_non_usual_function_declared_here : Note<
|
||||
"non-usual %0 declared here">;
|
||||
|
||||
// C++ literal operators
|
||||
def err_literal_operator_outside_namespace : Error<
|
||||
|
|
|
@ -10376,6 +10376,8 @@ private:
|
|||
ExprResult SemaBuiltinNontemporalOverloaded(ExprResult TheCallResult);
|
||||
ExprResult SemaAtomicOpsOverloaded(ExprResult TheCallResult,
|
||||
AtomicExpr::AtomicOp Op);
|
||||
ExprResult SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult,
|
||||
bool IsDelete);
|
||||
bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
|
||||
llvm::APSInt &Result);
|
||||
bool SemaBuiltinConstantArgRange(CallExpr *TheCall, int ArgNum,
|
||||
|
|
|
@ -2611,11 +2611,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
|||
case Builtin::BI__builtin_addressof:
|
||||
return RValue::get(EmitLValue(E->getArg(0)).getPointer());
|
||||
case Builtin::BI__builtin_operator_new:
|
||||
return EmitBuiltinNewDeleteCall(FD->getType()->castAs<FunctionProtoType>(),
|
||||
E->getArg(0), false);
|
||||
return EmitBuiltinNewDeleteCall(
|
||||
E->getCallee()->getType()->castAs<FunctionProtoType>(), E, false);
|
||||
case Builtin::BI__builtin_operator_delete:
|
||||
return EmitBuiltinNewDeleteCall(FD->getType()->castAs<FunctionProtoType>(),
|
||||
E->getArg(0), true);
|
||||
return EmitBuiltinNewDeleteCall(
|
||||
E->getCallee()->getType()->castAs<FunctionProtoType>(), E, true);
|
||||
|
||||
case Builtin::BI__noop:
|
||||
// __noop always evaluates to an integer literal zero.
|
||||
return RValue::get(ConstantInt::get(IntTy, 0));
|
||||
|
|
|
@ -1307,19 +1307,19 @@ static RValue EmitNewDeleteCall(CodeGenFunction &CGF,
|
|||
}
|
||||
|
||||
RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
|
||||
const Expr *Arg,
|
||||
const CallExpr *TheCall,
|
||||
bool IsDelete) {
|
||||
CallArgList Args;
|
||||
const Stmt *ArgS = Arg;
|
||||
EmitCallArgs(Args, *Type->param_type_begin(), llvm::makeArrayRef(ArgS));
|
||||
EmitCallArgs(Args, Type->getParamTypes(), TheCall->arguments());
|
||||
// Find the allocation or deallocation function that we're calling.
|
||||
ASTContext &Ctx = getContext();
|
||||
DeclarationName Name = Ctx.DeclarationNames
|
||||
.getCXXOperatorName(IsDelete ? OO_Delete : OO_New);
|
||||
|
||||
for (auto *Decl : Ctx.getTranslationUnitDecl()->lookup(Name))
|
||||
if (auto *FD = dyn_cast<FunctionDecl>(Decl))
|
||||
if (Ctx.hasSameType(FD->getType(), QualType(Type, 0)))
|
||||
return EmitNewDeleteCall(*this, cast<FunctionDecl>(Decl), Type, Args);
|
||||
return EmitNewDeleteCall(*this, FD, Type, Args);
|
||||
llvm_unreachable("predeclared global operator new/delete is missing");
|
||||
}
|
||||
|
||||
|
|
|
@ -2356,7 +2356,7 @@ public:
|
|||
CharUnits CookieSize = CharUnits());
|
||||
|
||||
RValue EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
|
||||
const Expr *Arg, bool IsDelete);
|
||||
const CallExpr *TheCallExpr, bool IsDelete);
|
||||
|
||||
llvm::Value *EmitCXXTypeidExpr(const CXXTypeidExpr *E);
|
||||
llvm::Value *EmitDynamicCast(Address V, const CXXDynamicCastExpr *DCE);
|
||||
|
|
|
@ -1801,12 +1801,21 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
|
|||
[this](Token &Tok, bool &HasLexedNextToken) -> int {
|
||||
IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
|
||||
diag::err_feature_check_malformed);
|
||||
const LangOptions &LangOpts = getLangOpts();
|
||||
if (!II)
|
||||
return false;
|
||||
else if (II->getBuiltinID() != 0)
|
||||
else if (II->getBuiltinID() != 0) {
|
||||
switch (II->getBuiltinID()) {
|
||||
case Builtin::BI__builtin_operator_new:
|
||||
case Builtin::BI__builtin_operator_delete:
|
||||
// denotes date of behavior change to support calling arbitrary
|
||||
// usual allocation and deallocation functions. Required by libc++
|
||||
return 201802;
|
||||
default:
|
||||
return true;
|
||||
else {
|
||||
const LangOptions &LangOpts = getLangOpts();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return llvm::StringSwitch<bool>(II->getName())
|
||||
.Case("__make_integer_seq", LangOpts.CPlusPlus)
|
||||
.Case("__type_pack_element", LangOpts.CPlusPlus)
|
||||
|
|
|
@ -1097,20 +1097,14 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
|
|||
return ExprError();
|
||||
break;
|
||||
case Builtin::BI__builtin_operator_new:
|
||||
case Builtin::BI__builtin_operator_delete:
|
||||
if (!getLangOpts().CPlusPlus) {
|
||||
Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language)
|
||||
<< (BuiltinID == Builtin::BI__builtin_operator_new
|
||||
? "__builtin_operator_new"
|
||||
: "__builtin_operator_delete")
|
||||
<< "C++";
|
||||
return ExprError();
|
||||
case Builtin::BI__builtin_operator_delete: {
|
||||
bool IsDelete = BuiltinID == Builtin::BI__builtin_operator_delete;
|
||||
ExprResult Res =
|
||||
SemaBuiltinOperatorNewDeleteOverloaded(TheCallResult, IsDelete);
|
||||
if (Res.isInvalid())
|
||||
CorrectDelayedTyposInExpr(TheCallResult.get());
|
||||
return Res;
|
||||
}
|
||||
// CodeGen assumes it can find the global new and delete to call,
|
||||
// so ensure that they are declared.
|
||||
DeclareGlobalNewDelete();
|
||||
break;
|
||||
|
||||
// check secure string manipulation functions where overflows
|
||||
// are detectable at compile time
|
||||
case Builtin::BI__builtin___memcpy_chk:
|
||||
|
|
|
@ -1443,7 +1443,7 @@ namespace {
|
|||
CUDAPref = S.IdentifyCUDAPreference(Caller, FD);
|
||||
}
|
||||
|
||||
operator bool() const { return FD; }
|
||||
explicit operator bool() const { return FD; }
|
||||
|
||||
bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize,
|
||||
bool WantAlign) const {
|
||||
|
@ -2271,7 +2271,6 @@ static bool resolveAllocationOverload(
|
|||
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,
|
||||
|
@ -3343,6 +3342,128 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
|
|||
return Result;
|
||||
}
|
||||
|
||||
static bool resolveBuiltinNewDeleteOverload(Sema &S, CallExpr *TheCall,
|
||||
bool IsDelete,
|
||||
FunctionDecl *&Operator) {
|
||||
|
||||
DeclarationName NewName = S.Context.DeclarationNames.getCXXOperatorName(
|
||||
IsDelete ? OO_Delete : OO_New);
|
||||
|
||||
LookupResult R(S, NewName, TheCall->getLocStart(), Sema::LookupOrdinaryName);
|
||||
S.LookupQualifiedName(R, S.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();
|
||||
|
||||
SmallVector<Expr *, 8> Args(TheCall->arg_begin(), TheCall->arg_end());
|
||||
OverloadCandidateSet Candidates(R.getNameLoc(),
|
||||
OverloadCandidateSet::CSK_Normal);
|
||||
for (LookupResult::iterator FnOvl = R.begin(), FnOvlEnd = R.end();
|
||||
FnOvl != FnOvlEnd; ++FnOvl) {
|
||||
// Even member operator new/delete are implicitly treated as
|
||||
// static, so don't use AddMemberCandidate.
|
||||
NamedDecl *D = (*FnOvl)->getUnderlyingDecl();
|
||||
|
||||
if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
|
||||
S.AddTemplateOverloadCandidate(FnTemplate, FnOvl.getPair(),
|
||||
/*ExplicitTemplateArgs=*/nullptr, Args,
|
||||
Candidates,
|
||||
/*SuppressUserConversions=*/false);
|
||||
continue;
|
||||
}
|
||||
|
||||
FunctionDecl *Fn = cast<FunctionDecl>(D);
|
||||
S.AddOverloadCandidate(Fn, FnOvl.getPair(), Args, Candidates,
|
||||
/*SuppressUserConversions=*/false);
|
||||
}
|
||||
|
||||
SourceRange Range = TheCall->getSourceRange();
|
||||
|
||||
// Do the resolution.
|
||||
OverloadCandidateSet::iterator Best;
|
||||
switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
|
||||
case OR_Success: {
|
||||
// Got one!
|
||||
FunctionDecl *FnDecl = Best->Function;
|
||||
assert(R.getNamingClass() == nullptr &&
|
||||
"class members should not be considered");
|
||||
|
||||
if (!FnDecl->isReplaceableGlobalAllocationFunction()) {
|
||||
S.Diag(R.getNameLoc(), diag::err_builtin_operator_new_delete_not_usual)
|
||||
<< (IsDelete ? 1 : 0) << Range;
|
||||
S.Diag(FnDecl->getLocation(), diag::note_non_usual_function_declared_here)
|
||||
<< R.getLookupName() << FnDecl->getSourceRange();
|
||||
return true;
|
||||
}
|
||||
|
||||
Operator = FnDecl;
|
||||
return false;
|
||||
}
|
||||
|
||||
case OR_No_Viable_Function:
|
||||
S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call)
|
||||
<< R.getLookupName() << Range;
|
||||
Candidates.NoteCandidates(S, OCD_AllCandidates, Args);
|
||||
return true;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
ExprResult
|
||||
Sema::SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult,
|
||||
bool IsDelete) {
|
||||
CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
|
||||
if (!getLangOpts().CPlusPlus) {
|
||||
Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language)
|
||||
<< (IsDelete ? "__builtin_operator_delete" : "__builtin_operator_new")
|
||||
<< "C++";
|
||||
return ExprError();
|
||||
}
|
||||
// CodeGen assumes it can find the global new and delete to call,
|
||||
// so ensure that they are declared.
|
||||
DeclareGlobalNewDelete();
|
||||
|
||||
FunctionDecl *OperatorNewOrDelete = nullptr;
|
||||
if (resolveBuiltinNewDeleteOverload(*this, TheCall, IsDelete,
|
||||
OperatorNewOrDelete))
|
||||
return ExprError();
|
||||
assert(OperatorNewOrDelete && "should be found");
|
||||
|
||||
TheCall->setType(OperatorNewOrDelete->getReturnType());
|
||||
for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) {
|
||||
QualType ParamTy = OperatorNewOrDelete->getParamDecl(i)->getType();
|
||||
InitializedEntity Entity =
|
||||
InitializedEntity::InitializeParameter(Context, ParamTy, false);
|
||||
ExprResult Arg = PerformCopyInitialization(
|
||||
Entity, TheCall->getArg(i)->getLocStart(), TheCall->getArg(i));
|
||||
if (Arg.isInvalid())
|
||||
return ExprError();
|
||||
TheCall->setArg(i, Arg.get());
|
||||
}
|
||||
auto Callee = dyn_cast<ImplicitCastExpr>(TheCall->getCallee());
|
||||
assert(Callee && Callee->getCastKind() == CK_BuiltinFnToFnPtr &&
|
||||
"Callee expected to be implicit cast to a builtin function pointer");
|
||||
Callee->setType(OperatorNewOrDelete->getType());
|
||||
|
||||
return TheCallResult;
|
||||
}
|
||||
|
||||
void Sema::CheckVirtualDtorCall(CXXDestructorDecl *dtor, SourceLocation Loc,
|
||||
bool IsDelete, bool CallCanBeVirtual,
|
||||
bool WarnOnNonAbstractTypes,
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s \
|
||||
// RUN: -faligned-allocation -fsized-deallocation -emit-llvm -o - \
|
||||
// RUN: | FileCheck %s
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
// Declare an 'operator new' template to tickle a bug in __builtin_operator_new.
|
||||
template<typename T> void *operator new(size_t, int (*)(T));
|
||||
|
||||
// Ensure that this declaration doesn't cause operator new to lose its
|
||||
// 'noalias' attribute.
|
||||
void *operator new(size_t);
|
||||
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
enum class align_val_t : size_t { __zero = 0,
|
||||
__max = (size_t)-1 };
|
||||
}
|
||||
std::nothrow_t nothrow;
|
||||
|
||||
// Declare the reserved placement operators.
|
||||
void *operator new(size_t, void*) throw();
|
||||
void operator delete(void*, void*) throw();
|
||||
void *operator new[](size_t, void*) throw();
|
||||
void operator delete[](void*, void*) throw();
|
||||
|
||||
// Declare the replaceable global allocation operators.
|
||||
void *operator new(size_t, const std::nothrow_t &) throw();
|
||||
void *operator new[](size_t, const std::nothrow_t &) throw();
|
||||
void operator delete(void *, const std::nothrow_t &) throw();
|
||||
void operator delete[](void *, const std::nothrow_t &) throw();
|
||||
|
||||
// Declare some other placement operators.
|
||||
void *operator new(size_t, void*, bool) throw();
|
||||
void *operator new[](size_t, void*, bool) throw();
|
||||
|
||||
|
||||
// CHECK-LABEL: define void @test_basic(
|
||||
extern "C" void test_basic() {
|
||||
// CHECK: call i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]
|
||||
// CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]]
|
||||
// CHECK: ret void
|
||||
__builtin_operator_delete(__builtin_operator_new(4));
|
||||
}
|
||||
// CHECK: declare noalias i8* @_Znwm(i64) [[ATTR_NOBUILTIN:#[^ ]*]]
|
||||
// CHECK: declare void @_ZdlPv(i8*) [[ATTR_NOBUILTIN_NOUNWIND:#[^ ]*]]
|
||||
|
||||
// CHECK-LABEL: define void @test_aligned_alloc(
|
||||
extern "C" void test_aligned_alloc() {
|
||||
// CHECK: call i8* @_ZnwmSt11align_val_t(i64 4, i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]
|
||||
// CHECK: call void @_ZdlPvSt11align_val_t({{.*}}, i64 4) [[ATTR_BUILTIN_DELETE:#[^ ]*]]
|
||||
__builtin_operator_delete(__builtin_operator_new(4, std::align_val_t(4)), std::align_val_t(4));
|
||||
}
|
||||
// CHECK: declare noalias i8* @_ZnwmSt11align_val_t(i64, i64) [[ATTR_NOBUILTIN:#[^ ]*]]
|
||||
// CHECK: declare void @_ZdlPvSt11align_val_t(i8*, i64) [[ATTR_NOBUILTIN_NOUNWIND:#[^ ]*]]
|
||||
|
||||
|
||||
// CHECK-LABEL: define void @test_sized_delete(
|
||||
extern "C" void test_sized_delete() {
|
||||
// CHECK: call i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]
|
||||
// CHECK: call void @_ZdlPvm({{.*}}, i64 4) [[ATTR_BUILTIN_DELETE:#[^ ]*]]
|
||||
__builtin_operator_delete(__builtin_operator_new(4), 4);
|
||||
}
|
||||
// CHECK: declare void @_ZdlPvm(i8*, i64) [[ATTR_NOBUILTIN_UNWIND:#[^ ]*]]
|
||||
|
||||
|
||||
// CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = {{[{].*}} nobuiltin {{.*[}]}}
|
||||
// CHECK-DAG: attributes [[ATTR_NOBUILTIN_NOUNWIND]] = {{[{].*}} nobuiltin nounwind {{.*[}]}}
|
||||
|
||||
// CHECK-DAG: attributes [[ATTR_BUILTIN_NEW]] = {{[{].*}} builtin {{.*[}]}}
|
||||
// CHECK-DAG: attributes [[ATTR_BUILTIN_DELETE]] = {{[{].*}} builtin {{.*[}]}}
|
|
@ -0,0 +1,153 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -std=c++03 -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -std=c++03 -faligned-allocation -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fsized-deallocation %s
|
||||
|
||||
#if !__has_builtin(__builtin_operator_new) || !__has_builtin(__builtin_operator_delete)
|
||||
#error builtins should always be available
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_operator_new) != 201802L || \
|
||||
__has_builtin(__builtin_operator_delete) != 201802L
|
||||
#error builtin should report updated value
|
||||
#endif
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
#if __cplusplus >= 201103L
|
||||
enum class align_val_t : size_t {};
|
||||
#else
|
||||
enum align_val_t { __zero = 0,
|
||||
__max = (size_t)-1 };
|
||||
#endif
|
||||
}
|
||||
std::nothrow_t nothrow;
|
||||
|
||||
void *operator new(size_t); // expected-note 1+ {{candidate function}}
|
||||
void operator delete(void *); // expected-note 1+ {{candidate function}}
|
||||
|
||||
// Declare the reserved placement operators.
|
||||
void *operator new(size_t, void*) throw(); // expected-note 1+ {{candidate function}}
|
||||
void operator delete(void *, void *)throw(); // expected-note 1+ {{candidate function}}
|
||||
void *operator new[](size_t, void*) throw();
|
||||
void operator delete[](void*, void*) throw();
|
||||
|
||||
// Declare the replaceable global allocation operators.
|
||||
void *operator new(size_t, const std::nothrow_t &) throw(); // expected-note 1+ {{candidate function}}
|
||||
void *operator new[](size_t, const std::nothrow_t &) throw();
|
||||
void operator delete(void *, const std::nothrow_t &)throw(); // expected-note 1+ {{candidate function}}
|
||||
void operator delete[](void *, const std::nothrow_t &) throw();
|
||||
|
||||
// aligned allocation and deallocation functions.
|
||||
void* operator new ( size_t count, std::align_val_t al); // expected-note 1+ {{candidate function}}
|
||||
void operator delete(void *, std::align_val_t); // expected-note 1+ {{candidate}}
|
||||
#ifndef __cpp_aligned_new
|
||||
// expected-note@-3 1+ {{non-usual 'operator new' declared here}}
|
||||
// expected-note@-3 1+ {{non-usual 'operator delete' declared here}}
|
||||
#endif
|
||||
void *operator new[](size_t count, std::align_val_t al);
|
||||
void operator delete[](void*, std::align_val_t);
|
||||
|
||||
void operator delete(void *, size_t); // expected-note 1+ {{candidate}}
|
||||
#ifndef __cpp_sized_deallocation
|
||||
// expected-note@-2 1+ {{non-usual 'operator delete' declared here}}
|
||||
#endif
|
||||
void operator delete[](void*, size_t);
|
||||
|
||||
// Declare some other placemenet operators.
|
||||
void *operator new(size_t, void*, bool) throw(); // expected-note 1+ {{candidate function}}
|
||||
void *operator new[](size_t, void*, bool) throw();
|
||||
|
||||
void *NP = 0;
|
||||
|
||||
void test_typo_in_args() {
|
||||
__builtin_operator_new(DNE); // expected-error {{undeclared identifier 'DNE'}}
|
||||
__builtin_operator_new(DNE, DNE2); // expected-error {{undeclared identifier 'DNE'}} expected-error {{'DNE2'}}
|
||||
__builtin_operator_delete(DNE); // expected-error {{'DNE'}}
|
||||
__builtin_operator_delete(DNE, DNE2); // expected-error {{'DNE'}} expected-error {{'DNE2'}}
|
||||
}
|
||||
|
||||
void test_arg_types() {
|
||||
__builtin_operator_new(NP); // expected-error {{no matching function for call to 'operator new'}}
|
||||
__builtin_operator_new(NP, std::align_val_t(0)); // expected-error {{no matching function for call to 'operator new'}}}
|
||||
}
|
||||
void test_return_type() {
|
||||
int w = __builtin_operator_new(42); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}}
|
||||
int y = __builtin_operator_delete(NP); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void'}}
|
||||
}
|
||||
|
||||
void test_aligned_new() {
|
||||
#ifdef __cpp_aligned_new
|
||||
void *p = __builtin_operator_new(42, std::align_val_t(2));
|
||||
__builtin_operator_delete(p, std::align_val_t(2));
|
||||
#else
|
||||
// FIXME: We've manually declared the aligned new/delete overloads,
|
||||
// but LangOpts::AlignedAllocation is false. Should our overloads be considered
|
||||
// usual allocation/deallocation functions?
|
||||
void *p = __builtin_operator_new(42, std::align_val_t(2)); // expected-error {{call to '__builtin_operator_new' selects non-usual allocation function}}
|
||||
__builtin_operator_delete(p, std::align_val_t(2)); // expected-error {{call to '__builtin_operator_delete' selects non-usual deallocation function}}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_sized_delete() {
|
||||
#ifdef __cpp_sized_deallocation
|
||||
__builtin_operator_delete(NP, 4);
|
||||
#else
|
||||
__builtin_operator_delete(NP, 4); // expected-error {{call to '__builtin_operator_delete' selects non-usual deallocation function}}
|
||||
#endif
|
||||
}
|
||||
|
||||
void *operator new(size_t, bool); // expected-note 1+ {{candidate}}
|
||||
// expected-note@-1 {{non-usual 'operator new' declared here}}
|
||||
void operator delete(void *, bool); // expected-note 1+ {{candidate}}
|
||||
// expected-note@-1 {{non-usual 'operator delete' declared here}}
|
||||
|
||||
void test_non_usual() {
|
||||
__builtin_operator_new(42, true); // expected-error {{call to '__builtin_operator_new' selects non-usual allocation function}}
|
||||
__builtin_operator_delete(NP, false); // expected-error {{call to '__builtin_operator_delete' selects non-usual deallocation function}}
|
||||
}
|
||||
|
||||
template <int ID>
|
||||
struct Tag {};
|
||||
struct ConvertsToTypes {
|
||||
operator std::align_val_t() const;
|
||||
operator Tag<0>() const;
|
||||
};
|
||||
|
||||
void *operator new(size_t, Tag<0>); // expected-note 0+ {{candidate}}
|
||||
void operator delete(void *, Tag<0>); // expected-note 0+ {{candidate}}
|
||||
|
||||
void test_ambiguous() {
|
||||
#ifdef __cpp_aligned_new
|
||||
ConvertsToTypes cvt;
|
||||
__builtin_operator_new(42, cvt); // expected-error {{call to 'operator new' is ambiguous}}
|
||||
__builtin_operator_delete(NP, cvt); // expected-error {{call to 'operator delete' is ambiguous}}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_no_args() {
|
||||
__builtin_operator_new(); // expected-error {{no matching function for call to 'operator new'}}
|
||||
__builtin_operator_delete(); // expected-error {{no matching function for call to 'operator delete'}}
|
||||
}
|
||||
|
||||
void test_no_matching_fn() {
|
||||
Tag<1> tag;
|
||||
__builtin_operator_new(42, tag); // expected-error {{no matching function for call to 'operator new'}}
|
||||
__builtin_operator_delete(NP, tag); // expected-error {{no matching function for call to 'operator delete'}}
|
||||
}
|
||||
|
||||
template <class Tp, class Up, class RetT>
|
||||
void test_dependent_call(Tp new_arg, Up delete_arg, RetT) {
|
||||
RetT ret = __builtin_operator_new(new_arg);
|
||||
__builtin_operator_delete(delete_arg);
|
||||
}
|
||||
template void test_dependent_call(int, int*, void*);
|
||||
|
||||
void test_const_attribute() {
|
||||
__builtin_operator_new(42); // expected-warning {{ignoring return value of function declared with const attribute}}
|
||||
#ifdef __cpp_aligned_new
|
||||
__builtin_operator_new(42, std::align_val_t(8)); // expected-warning {{ignoring return value of function declared with const attribute}}
|
||||
#endif
|
||||
}
|
Loading…
Reference in New Issue