diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/AST/Decl.h | 2 | ||||
-rw-r--r-- | clang/include/clang/Basic/LangOptions.def | 1 | ||||
-rw-r--r-- | clang/include/clang/Driver/CC1Options.td | 2 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 6 | ||||
-rw-r--r-- | clang/lib/AST/Decl.cpp | 5 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInvocation.cpp | 1 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 194 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/cxx1y-sized-deallocation.cpp | 83 | ||||
-rw-r--r-- | clang/test/SemaCXX/cxx1y-sized-deallocation.cpp | 17 | ||||
-rw-r--r-- | clang/www/cxx_status.html | 7 |
10 files changed, 260 insertions, 58 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c68416b5cd0..ec43cb57271 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1775,8 +1775,10 @@ public: /// void *operator new[](size_t); /// void *operator new[](size_t, const std::nothrow_t &) noexcept; /// void operator delete(void *) noexcept; + /// void operator delete(void *, std::size_t) noexcept; [C++1y] /// void operator delete(void *, const std::nothrow_t &) noexcept; /// void operator delete[](void *) noexcept; + /// void operator delete[](void *, std::size_t) noexcept; [C++1y] /// void operator delete[](void *, const std::nothrow_t &) noexcept; /// These functions have special behavior under C++1y [expr.new]: /// An implementation is allowed to omit a call to a replaceable global diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index f992a3e1785..b637ec2c542 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -123,6 +123,7 @@ LANGOPT(CUDA , 1, 0, "CUDA") LANGOPT(OpenMP , 1, 0, "OpenMP support") LANGOPT(AssumeSaneOperatorNew , 1, 1, "implicit __attribute__((malloc)) for C++'s new operators") +LANGOPT(SizedDeallocation , 1, 0, "enable sized deallocation functions") BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision") BENIGN_LANGOPT(DumpRecordLayouts , 1, 0, "dumping the layout of IRgen'd records") BENIGN_LANGOPT(DumpRecordLayoutsSimple , 1, 0, "dumping the layout of IRgen'd records in a simple form") diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index c04059896a7..d27ad6a9600 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -474,6 +474,8 @@ def fdeprecated_macro : Flag<["-"], "fdeprecated-macro">, HelpText<"Defines the __DEPRECATED macro">; def fno_deprecated_macro : Flag<["-"], "fno-deprecated-macro">, HelpText<"Undefines the __DEPRECATED macro">; +def fsized_deallocation : Flag<["-"], "fsized-deallocation">, + HelpText<"Enable C++1y sized global deallocation functions">; //===----------------------------------------------------------------------===// // Header Search Options diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 66772c07ae1..116b427266e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4133,12 +4133,16 @@ public: bool Diagnose = true); void DeclareGlobalNewDelete(); void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return, - QualType Argument, + QualType Param1, + QualType Param2 = QualType(), bool addMallocAttr = false); bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, DeclarationName Name, FunctionDecl* &Operator, bool Diagnose = true); + FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc, + bool CanProvideSize, + DeclarationName Name); /// ActOnCXXDelete - Parsed a C++ 'delete' expression ExprResult ActOnCXXDelete(SourceLocation StartLoc, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 587b18e7408..57bf94c0f0b 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2310,8 +2310,11 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const { return true; // Otherwise, we're looking for a second parameter whose type is - // 'const std::nothrow_t &'. + // 'const std::nothrow_t &', or, in C++1y, 'std::size_t'. QualType Ty = FPT->getArgType(1); + ASTContext &Ctx = getASTContext(); + if (Ctx.getLangOpts().SizedDeallocation && Ty == Ctx.getSizeType()) + return true; if (!Ty->isReferenceType()) return false; Ty = Ty->getPointeeType(); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index d25801a7a7e..58a6e25d5ca 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1292,6 +1292,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; Opts.NoMathBuiltin = Args.hasArg(OPT_fno_math_builtin); Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); + Opts.SizedDeallocation = Args.hasArg(OPT_fsized_deallocation); Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index e6dd5fb1af4..f13ad64746f 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1539,16 +1539,23 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, /// \brief Determine whether the given function is a non-placement /// deallocation function. -static bool isNonPlacementDeallocationFunction(FunctionDecl *FD) { +static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) { if (FD->isInvalidDecl()) return false; if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) return Method->isUsualDeallocationFunction(); - return ((FD->getOverloadedOperator() == OO_Delete || - FD->getOverloadedOperator() == OO_Array_Delete) && - FD->getNumParams() == 1); + if (FD->getOverloadedOperator() != OO_Delete && + FD->getOverloadedOperator() != OO_Array_Delete) + return false; + + if (FD->getNumParams() == 1) + return true; + + return S.getLangOpts().SizedDeallocation && FD->getNumParams() == 2 && + S.Context.hasSameUnqualifiedType(FD->getParamDecl(1)->getType(), + S.Context.getSizeType()); } /// FindAllocationFunctions - Finds the overloads of operator new and delete @@ -1723,9 +1730,28 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, DEnd = FoundDelete.end(); D != DEnd; ++D) { if (FunctionDecl *Fn = dyn_cast<FunctionDecl>((*D)->getUnderlyingDecl())) - if (isNonPlacementDeallocationFunction(Fn)) + if (isNonPlacementDeallocationFunction(*this, Fn)) Matches.push_back(std::make_pair(D.getPair(), Fn)); } + + // 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 uusal deallocation function"); + } } // C++ [expr.new]p20: @@ -1742,12 +1768,13 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // selected as a match for the allocation function, the program // is ill-formed. if (!PlaceArgs.empty() && getLangOpts().CPlusPlus11 && - isNonPlacementDeallocationFunction(OperatorDelete)) { + isNonPlacementDeallocationFunction(*this, OperatorDelete)) { Diag(StartLoc, diag::err_placement_new_non_placement_delete) << SourceRange(PlaceArgs.front()->getLocStart(), PlaceArgs.back()->getLocEnd()); - Diag(OperatorDelete->getLocation(), diag::note_previous_decl) - << DeleteName; + if (!OperatorDelete->isImplicit()) + Diag(OperatorDelete->getLocation(), diag::note_previous_decl) + << DeleteName; } else { CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(), Matches[0].first); @@ -1874,13 +1901,19 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, /// void* operator new[](std::size_t) throw(std::bad_alloc); /// void operator delete(void *) throw(); /// void operator delete[](void *) throw(); -/// // C++0x: +/// // C++11: /// void* operator new(std::size_t); /// void* operator new[](std::size_t); -/// void operator delete(void *); -/// void operator delete[](void *); +/// void operator delete(void *) noexcept; +/// void operator delete[](void *) noexcept; +/// // C++1y: +/// void* operator new(std::size_t); +/// void* operator new[](std::size_t); +/// void operator delete(void *) noexcept; +/// void operator delete[](void *) noexcept; +/// void operator delete(void *, std::size_t) noexcept; +/// void operator delete[](void *, std::size_t) noexcept; /// @endcode -/// C++0x operator delete is implicitly noexcept. /// Note that the placement and nothrow forms of new are *not* implicitly /// declared. Their use requires including \<new\>. void Sema::DeclareGlobalNewDelete() { @@ -1897,11 +1930,18 @@ void Sema::DeclareGlobalNewDelete() { // void* operator new[](std::size_t) throw(std::bad_alloc); // void operator delete(void*) throw(); // void operator delete[](void*) throw(); - // C++0x: + // C++11: + // void* operator new(std::size_t); + // void* operator new[](std::size_t); + // void operator delete(void*) noexcept; + // void operator delete[](void*) noexcept; + // C++1y: // void* operator new(std::size_t); // void* operator new[](std::size_t); - // void operator delete(void*); - // void operator delete[](void*); + // void operator delete(void*) noexcept; + // void operator delete[](void*) noexcept; + // void operator delete(void*, std::size_t) noexcept; + // void operator delete[](void*, std::size_t) noexcept; // // These implicit declarations introduce only the function names operator // new, operator new[], operator delete, operator delete[]. @@ -1910,8 +1950,6 @@ void Sema::DeclareGlobalNewDelete() { // "std" or "bad_alloc" as necessary to form the exception specification. // However, we do not make these implicit declarations visible to name // lookup. - // Note that the C++0x versions of operator delete are deallocation functions, - // and thus are implicitly noexcept. if (!StdBadAlloc && !getLangOpts().CPlusPlus11) { // The "std::bad_alloc" class has not yet been declared, so build it // implicitly. @@ -1931,24 +1969,34 @@ void Sema::DeclareGlobalNewDelete() { DeclareGlobalAllocationFunction( Context.DeclarationNames.getCXXOperatorName(OO_New), - VoidPtr, SizeT, AssumeSaneOperatorNew); + VoidPtr, SizeT, QualType(), AssumeSaneOperatorNew); DeclareGlobalAllocationFunction( Context.DeclarationNames.getCXXOperatorName(OO_Array_New), - VoidPtr, SizeT, AssumeSaneOperatorNew); + VoidPtr, SizeT, QualType(), AssumeSaneOperatorNew); DeclareGlobalAllocationFunction( Context.DeclarationNames.getCXXOperatorName(OO_Delete), Context.VoidTy, VoidPtr); DeclareGlobalAllocationFunction( Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete), Context.VoidTy, VoidPtr); + if (getLangOpts().SizedDeallocation) { + DeclareGlobalAllocationFunction( + Context.DeclarationNames.getCXXOperatorName(OO_Delete), + Context.VoidTy, VoidPtr, Context.getSizeType()); + DeclareGlobalAllocationFunction( + Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete), + Context.VoidTy, VoidPtr, Context.getSizeType()); + } } /// DeclareGlobalAllocationFunction - Declares a single implicit global /// allocation function if it doesn't already exist. void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, - QualType Return, QualType Argument, + QualType Return, + QualType Param1, QualType Param2, bool AddMallocAttr) { DeclContext *GlobalCtx = Context.getTranslationUnitDecl(); + unsigned NumParams = Param2.isNull() ? 1 : 2; // Check if this function is already declared. DeclContext::lookup_result R = GlobalCtx->lookup(Name); @@ -1957,12 +2005,18 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, // Only look at non-template functions, as it is the predefined, // non-templated allocation function we are trying to declare here. if (FunctionDecl *Func = dyn_cast<FunctionDecl>(*Alloc)) { - if (Func->getNumParams() == 1) { - QualType InitialParamType = - Context.getCanonicalType( - Func->getParamDecl(0)->getType().getUnqualifiedType()); + if (Func->getNumParams() == NumParams) { + QualType InitialParam1Type = + Context.getCanonicalType(Func->getParamDecl(0) + ->getType().getUnqualifiedType()); + QualType InitialParam2Type = + NumParams == 2 + ? Context.getCanonicalType(Func->getParamDecl(1) + ->getType().getUnqualifiedType()) + : QualType(); // FIXME: Do we need to check for default arguments here? - if (InitialParamType == Argument) { + if (InitialParam1Type == Param1 && + (NumParams == 1 || InitialParam2Type == Param2)) { if (AddMallocAttr && !Func->hasAttr<MallocAttr>()) Func->addAttr(::new (Context) MallocAttr(SourceLocation(), Context)); @@ -1997,7 +2051,10 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, EST_BasicNoexcept : EST_DynamicNone; } - QualType FnType = Context.getFunctionType(Return, Argument, EPI); + QualType Params[] = { Param1, Param2 }; + + QualType FnType = Context.getFunctionType( + Return, ArrayRef<QualType>(Params, NumParams), EPI); FunctionDecl *Alloc = FunctionDecl::Create(Context, GlobalCtx, SourceLocation(), SourceLocation(), Name, @@ -2007,11 +2064,13 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, if (AddMallocAttr) Alloc->addAttr(::new (Context) MallocAttr(SourceLocation(), Context)); - ParmVarDecl *Param = ParmVarDecl::Create(Context, Alloc, SourceLocation(), - SourceLocation(), 0, - Argument, /*TInfo=*/0, - SC_None, 0); - Alloc->setParams(Param); + ParmVarDecl *ParamDecls[2]; + for (unsigned I = 0; I != NumParams; ++I) + ParamDecls[I] = ParmVarDecl::Create(Context, Alloc, SourceLocation(), + SourceLocation(), 0, + Params[I], /*TInfo=*/0, + SC_None, 0); + Alloc->setParams(ArrayRef<ParmVarDecl*>(ParamDecls, NumParams)); // FIXME: Also add this declaration to the IdentifierResolver, but // make sure it is at the end of the chain to coincide with the @@ -2019,6 +2078,48 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, Context.getTranslationUnitDecl()->addDecl(Alloc); } +FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc, + bool CanProvideSize, + 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); + } + + // 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 uusal deallocation function"); + } + + assert(Matches.size() == 1 && + "unexpectedly have multiple usual deallocation functions"); + return Matches.front(); +} + bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, DeclarationName Name, FunctionDecl* &Operator, bool Diagnose) { @@ -2093,17 +2194,7 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, } // Look for a global declaration. - DeclareGlobalNewDelete(); - DeclContext *TUDecl = Context.getTranslationUnitDecl(); - - CXXNullPtrLiteralExpr Null(Context.VoidPtrTy, SourceLocation()); - Expr *DeallocArgs[1] = { &Null }; - if (FindAllocationOverload(StartLoc, SourceRange(), Name, - DeallocArgs, TUDecl, !Diagnose, - Operator, Diagnose)) - return true; - - assert(Operator && "Did not find a deallocation function!"); + Operator = FindUsualDeallocationFunction(StartLoc, true, Name); return false; } @@ -2291,20 +2382,13 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, } - if (!OperatorDelete) { + if (!OperatorDelete) // Look for a global declaration. - DeclareGlobalNewDelete(); - DeclContext *TUDecl = Context.getTranslationUnitDecl(); - Expr *Arg = Ex.get(); - if (!Context.hasSameType(Arg->getType(), Context.VoidPtrTy)) - Arg = ImplicitCastExpr::Create(Context, Context.VoidPtrTy, - CK_BitCast, Arg, 0, VK_RValue); - Expr *DeallocArgs[1] = { Arg }; - if (FindAllocationOverload(StartLoc, SourceRange(), DeleteName, - DeallocArgs, TUDecl, /*AllowMissing=*/false, - OperatorDelete)) - return ExprError(); - } + OperatorDelete = FindUsualDeallocationFunction( + StartLoc, !RequireCompleteType(StartLoc, Pointee, 0) && + (!ArrayForm || UsualArrayDeleteWantsSize || + Pointee.isDestructedType()), + DeleteName); MarkFunctionReferenced(StartLoc, OperatorDelete); diff --git a/clang/test/CodeGenCXX/cxx1y-sized-deallocation.cpp b/clang/test/CodeGenCXX/cxx1y-sized-deallocation.cpp new file mode 100644 index 00000000000..767878365e8 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx1y-sized-deallocation.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -std=c++1y -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++1y %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNSIZED + +// CHECK-UNSIZED-NOT: _ZdlPvm +// CHECK-UNSIZED-NOT: _ZdaPvm + +typedef decltype(sizeof(0)) size_t; + +typedef int A; +struct B { int n; }; +struct C { ~C() {} }; +struct D { D(); virtual ~D() {} }; +struct E { + void *operator new(size_t); + void *operator new[](size_t); + void operator delete(void *) noexcept; + void operator delete[](void *) noexcept; +}; +struct F { + void *operator new(size_t); + void *operator new[](size_t); + void operator delete(void *, size_t) noexcept; + void operator delete[](void *, size_t) noexcept; +}; + +template<typename T> T get(); + +template<typename T> +void del() { + ::delete get<T*>(); + ::delete[] get<T*>(); +} + +template void del<A>(); +template void del<B>(); +template void del<C>(); +template void del<D>(); +template void del<E>(); +template void del<F>(); + +D::D() {} + +// CHECK-LABEL: define weak_odr void @_Z3delIiEvv() +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 4) +// CHECK: call void @_ZdaPv(i8* %{{[^ ]*}}) + +// CHECK-LABEL: define weak_odr void @_Z3delI1BEvv() +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 4) +// CHECK: call void @_ZdaPv(i8* %{{[^ ]*}}) + +// CHECK-LABEL: define weak_odr void @_Z3delI1CEvv() +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 1) +// CHECK: mul i64 1, %{{[^ ]*}} +// CHECK: add i64 %{{[^ ]*}}, 8 +// CHECK: call void @_ZdaPvm(i8* %{{[^ ]*}}, i64 %{{[^ ]*}}) + +// CHECK-LABEL: define weak_odr void @_Z3delI1DEvv() +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 8) +// CHECK: mul i64 8, %{{[^ ]*}} +// CHECK: add i64 %{{[^ ]*}}, 8 +// CHECK: call void @_ZdaPvm(i8* %{{[^ ]*}}, i64 %{{[^ ]*}}) + +// CHECK-LABEL: define weak_odr void @_Z3delI1EEvv() +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 1) +// CHECK: call void @_ZdaPv(i8* %{{[^ ]*}}) + +// CHECK-LABEL: define weak_odr void @_Z3delI1FEvv() +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 1) +// CHECK: mul i64 1, %{{[^ ]*}} +// CHECK: add i64 %{{[^ ]*}}, 8 +// CHECK: call void @_ZdaPvm(i8* %{{[^ ]*}}, i64 %{{[^ ]*}}) + +// CHECK-LABEL: define void @_Z10member_delv() +// CHECK-NOT: Zdl +// CHECK: call void %{{[^ ]*}}(%{{[^ ]*}}* % +// CHECK-NOT: Zdl +// CHECK: } +void member_del() { + delete get<D*>(); +} + +// CHECK-LABEL: define linkonce_odr void @_ZN1DD0Ev(%{{[^ ]*}}* %this) +// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 8) diff --git a/clang/test/SemaCXX/cxx1y-sized-deallocation.cpp b/clang/test/SemaCXX/cxx1y-sized-deallocation.cpp new file mode 100644 index 00000000000..81a8eeb6df9 --- /dev/null +++ b/clang/test/SemaCXX/cxx1y-sized-deallocation.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -std=c++1y -verify %s -fsized-deallocation -fexceptions -fcxx-exceptions + +using size_t = decltype(sizeof(0)); + +void f(void *p, void *q) { + // OK, implicitly declared. + operator delete(p, 8); + operator delete[](q, 12); + static_assert(noexcept(operator delete(p, 8)), ""); + static_assert(noexcept(operator delete[](q, 12)), ""); +} + +void *operator new(size_t bad, size_t idea); +struct S { S() { throw 0; } }; +void g() { + new (123) S; // expected-error {{'new' expression with placement arguments refers to non-placement 'operator delete'}} +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index b32e5663c64..398d4517445 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -476,10 +476,15 @@ available.</p> <tr> <td>C++ Sized Deallocation</td> <td><!--<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3778.html">-->N3778<!--</a>--></td> - <td class="none" align="center">No</td> + <td class="partial" align="center">Partial <a href="#n3778">(1)</a></td> </tr> </table> +<p> +<span id="n3778">(1): Use the experimental <tt>-Xclang -fsized-deallocation</tt> +flag to enable this feature.</span><br> +</p> + </div> </body> </html> |