Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7654,6 +7654,10 @@ def err_destroying_operator_delete_not_usual : Error< "destroying operator delete can have only an optional size and optional " "alignment parameter">; +def warn_destroying_operator_delete_ignored : Warning< + "destroying operator delete is ignored prior to C++2a">, InGroup; +def note_enable_destroying_delete_extension : Note< + "use -fdestroying-delete to enable destroying operator delete">; 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< Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -219,6 +219,7 @@ LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") LANGOPT(NewAlignOverride , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'") +LANGOPT(DestroyingDelete , 1, 0, "destroying delete") LANGOPT(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts") BENIGN_LANGOPT(ModulesCodegen , 1, 0, "Modules code generation") BENIGN_LANGOPT(ModulesDebugInfo , 1, 0, "Modules debug info") Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1517,6 +1517,8 @@ def : Flag<["-"], "faligned-new">, Alias; def : Flag<["-"], "fno-aligned-new">, Alias; def faligned_new_EQ : Joined<["-"], "faligned-new=">; +def fdestroying_delete : Flag<["-"], "fdestroying-delete">, Flags<[DriverOption, CC1Option]>, + HelpText<"Enable C++20 destroying delete">, Group; def fobjc_legacy_dispatch : Flag<["-"], "fobjc-legacy-dispatch">, Group; def fobjc_new_property : Flag<["-"], "fobjc-new-property">, Group; Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -2059,13 +2059,18 @@ return true; unsigned UsualParams = 1; + ASTContext &Context = getASTContext(); + // C++ P0722: // A destroying operator delete is a usual deallocation function if // removing the std::destroying_delete_t parameter and changing the // first parameter type from T* to void* results in the signature of // a usual deallocation function. - if (isDestroyingOperatorDelete()) + if (isDestroyingOperatorDelete()) { + if (!getASTContext().getLangOpts().DestroyingDelete) + return false; ++UsualParams; + } // C++ <=14 [basic.stc.dynamic.deallocation]p2: // [...] If class T does not declare such an operator delete but does @@ -2077,7 +2082,6 @@ // (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 (UsualParams < getNumParams() && Context.hasSameUnqualifiedType(getParamDecl(UsualParams)->getType(), Context.getSizeType())) @@ -2097,7 +2101,7 @@ // handle when destroying delete is used prior to C++17? if (Context.getLangOpts().CPlusPlus17 || Context.getLangOpts().AlignedAllocation || - isDestroyingOperatorDelete()) + Context.getLangOpts().DestroyingDelete) return true; // This function is a usual deallocation function if there are no Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -4855,6 +4855,10 @@ CmdArgs.push_back( Args.MakeArgString(Twine("-fnew-alignment=") + A->getValue())); + // -fdestroying-delete is on by default in C++2a onwards and otherwise + // it's off by default. + Args.AddAllArgs(CmdArgs, options::OPT_fdestroying_delete); + // -fconstant-cfstrings is default, and may be subject to argument translation // on Darwin. if (!Args.hasFlag(options::OPT_fconstant_cfstrings, Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2172,6 +2172,7 @@ Opts.CXXOperatorNames = Opts.CPlusPlus; Opts.AlignedAllocation = Opts.CPlusPlus17; + Opts.DestroyingDelete = Opts.CPlusPlus2a; Opts.DollarIdents = !Opts.AsmPreprocessor; } @@ -2633,6 +2634,8 @@ << A->getValue(); Opts.NewAlignOverride = 0; } + Opts.DestroyingDelete = + Opts.DestroyingDelete || Args.hasArg(OPT_fdestroying_delete); Opts.ConceptsTS = Args.hasArg(OPT_fconcepts_ts); Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); Index: lib/Frontend/InitPreprocessor.cpp =================================================================== --- lib/Frontend/InitPreprocessor.cpp +++ lib/Frontend/InitPreprocessor.cpp @@ -542,7 +542,8 @@ // C++20 features. if (LangOpts.Char8) Builder.defineMacro("__cpp_char8_t", "201811L"); - Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); + if (LangOpts.DestroyingDelete) + Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); // TS features. if (LangOpts.ConceptsTS) Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -13311,14 +13311,23 @@ diag::err_operator_delete_param_type)) return true; - // C++ P0722: - // A destroying operator delete shall be a usual deallocation function. - if (MD && !MD->getParent()->isDependentContext() && - MD->isDestroyingOperatorDelete() && - !SemaRef.isUsualDeallocationFunction(MD)) { - SemaRef.Diag(MD->getLocation(), - diag::err_destroying_operator_delete_not_usual); - return true; + if (MD && MD->isDestroyingOperatorDelete()) { + // C++ P0722: + // A destroying operator delete shall be a usual deallocation function. + if (SemaRef.getLangOpts().DestroyingDelete && + !MD->getParent()->isDependentContext() && + !SemaRef.isUsualDeallocationFunction(MD)) { + SemaRef.Diag(MD->getLocation(), + diag::err_destroying_operator_delete_not_usual); + return true; + } + if (!SemaRef.LangOpts.DestroyingDelete) { + SemaRef.Diag(MD->getLocation(), + diag::warn_destroying_operator_delete_ignored) + << MD->getParamDecl(1)->getSourceRange(); + SemaRef.Diag(MD->getLocation(), + diag::note_enable_destroying_delete_extension); + } } return false; Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -1506,7 +1506,7 @@ if (!FD) return; unsigned NumBaseParams = 1; - if (FD->isDestroyingOperatorDelete()) { + if (FD->isDestroyingOperatorDelete() && S.LangOpts.DestroyingDelete) { Destroying = true; ++NumBaseParams; } Index: test/CodeGenCXX/cxx2a-destroying-delete.cpp =================================================================== --- test/CodeGenCXX/cxx2a-destroying-delete.cpp +++ test/CodeGenCXX/cxx2a-destroying-delete.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM +// RUN: %clang_cc1 -std=c++11 -faligned-allocation -fdestroying-delete -emit-llvm %s -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM // RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple x86_64-windows -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-MSABI namespace std { Index: test/SemaCXX/cxx2a-destroying-delete.cpp =================================================================== --- test/SemaCXX/cxx2a-destroying-delete.cpp +++ test/SemaCXX/cxx2a-destroying-delete.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -std=c++2a -fexceptions -verify %s // RUN: %clang_cc1 -std=c++2a -verify %s +// RUN: %clang_cc1 -std=c++17 -fexceptions -fdestroying-delete -verify %s namespace std { using size_t = decltype(sizeof(0)); Index: test/SemaCXX/extended-usual-deallocation-functions.cpp =================================================================== --- test/SemaCXX/extended-usual-deallocation-functions.cpp +++ test/SemaCXX/extended-usual-deallocation-functions.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fexceptions -std=c++2a -fsized-deallocation -fno-aligned-allocation -verify %s -// RUN: %clang_cc1 -fexceptions -std=c++17 -fsized-deallocation -fno-aligned-allocation -verify %s -// RUN: %clang_cc1 -fexceptions -std=c++14 -fsized-deallocation -faligned-allocation -DHAS_ALIGN -verify %s -// RUN: %clang_cc1 -fexceptions -std=c++11 -fsized-deallocation -faligned-allocation -DHAS_ALIGN -verify %s +// RUN: %clang_cc1 -fexceptions -std=c++17 -fdestroying-delete -fsized-deallocation -fno-aligned-allocation -verify %s +// RUN: %clang_cc1 -fexceptions -std=c++14 -fdestroying-delete -fsized-deallocation -faligned-allocation -DHAS_ALIGN -verify %s +// RUN: %clang_cc1 -fexceptions -std=c++11 -fdestroying-delete -fsized-deallocation -faligned-allocation -DHAS_ALIGN -verify %s // Test that we handle aligned deallocation, sized deallocation, and destroying // delete as usual deallocation functions even if they are used as extensions Index: test/SemaCXX/warn-destroying-delete-extension.cpp =================================================================== --- /dev/null +++ test/SemaCXX/warn-destroying-delete-extension.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s +namespace std { +struct destroying_delete_t { +}; +} // namespace std +struct C { + // expected-warning@+2 {{destroying operator delete is ignored prior to C++2a}} + // expected-note@+1 {{use -fdestroying-delete to enable destroying operator delete}} + void operator delete(C *, std::destroying_delete_t); +};