diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 30552be9b381..08b34a75aa60 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2019,7 +2019,10 @@ public: /// These functions have special behavior under C++1y [expr.new]: /// An implementation is allowed to omit a call to a replaceable global /// allocation function. [...] - bool isReplaceableGlobalAllocationFunction() const; + /// + /// If this function is an aligned allocation/deallocation function, return + /// true through IsAligned. + bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const; /// Compute the language linkage. LanguageLinkage getLanguageLinkage() const; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 464c2425a1f1..3a0564806b32 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -311,6 +311,7 @@ def : DiagGroup<"nonportable-cfstrings">; def NonVirtualDtor : DiagGroup<"non-virtual-dtor">; def : DiagGroup<"effc++", [NonVirtualDtor]>; def OveralignedType : DiagGroup<"over-aligned">; +def AlignedAllocationUnavailable : DiagGroup<"aligned-allocation-unavailable">; def OldStyleCast : DiagGroup<"old-style-cast">; def : DiagGroup<"old-style-definition">; def OutOfLineDeclaration : DiagGroup<"out-of-line-declaration">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6224f13ce762..7b1671bb8777 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6407,6 +6407,12 @@ def warn_overaligned_type : Warning< "type %0 requires %1 bytes of alignment and the default allocator only " "guarantees %2 bytes">, InGroup, DefaultIgnore; +def warn_aligned_allocation_unavailable :Warning< + "aligned %select{allocation|deallocation}0 function of type '%1' possibly " + "unavailable on %2">, InGroup, DefaultError; +def note_silence_unligned_allocation_unavailable : Note< + "if you supply your own aligned allocation functions, use " + "-Wno-aligned-allocation-unavailable to silence this diagnostic">; def err_conditional_void_nonvoid : Error< "%select{left|right}1 operand to ? is void, but %select{right|left}1 operand " diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 60c8a68cd2e9..dfdad108922a 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -199,6 +199,7 @@ LANGOPT(CUDADeviceApproxTranscendentals, 1, 0, "using approximate transcendental LANGOPT(SizedDeallocation , 1, 0, "sized deallocation") 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(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts") BENIGN_LANGOPT(ModulesCodegen , 1, 0, "Modules code generation") diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index c478cfc7833e..ab790c8a0a4b 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -568,6 +568,9 @@ def find_pch_source_EQ : Joined<["-"], "find-pch-source=">, def fno_pch_timestamp : Flag<["-"], "fno-pch-timestamp">, HelpText<"Disable inclusion of timestamp in precompiled headers">; +def aligned_alloc_unavailable : Flag<["-"], "faligned-alloc-unavailable">, + HelpText<"Aligned allocation/deallocation functions are unavailable">; + //===----------------------------------------------------------------------===// // Language Options //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 9862f4f26473..8677b1155a60 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2630,7 +2630,7 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const { return (proto->getParamType(1).getCanonicalType() == Context.VoidPtrTy); } -bool FunctionDecl::isReplaceableGlobalAllocationFunction() const { +bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const { if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName) return false; if (getDeclName().getCXXOverloadedOperator() != OO_New && @@ -2676,8 +2676,11 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const { // 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()) + if (Ctx.getLangOpts().AlignedAllocation && !Ty.isNull() && Ty->isAlignValT()) { + if (IsAligned) + *IsAligned = true; Consume(); + } // Finally, if this is not a sized delete, the final parameter can // be a 'const std::nothrow_t&'. diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index e41b50c40b28..bfe685e70df7 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1667,6 +1667,27 @@ void MachO::AddLinkRuntimeLibArgs(const ArgList &Args, AddLinkRuntimeLib(Args, CmdArgs, CompilerRT, false, true); } +bool Darwin::isAlignedAllocationUnavailable() const { + switch (TargetPlatform) { + case MacOS: // Earlier than 10.13. + return TargetVersion < VersionTuple(10U, 13U, 0U); + case IPhoneOS: + case IPhoneOSSimulator: + case TvOS: + case TvOSSimulator: // Earlier than 11.0. + return TargetVersion < VersionTuple(11U, 0U, 0U); + case WatchOS: + case WatchOSSimulator: // Earlier than 4.0. + return TargetVersion < VersionTuple(4U, 0U, 0U); + } +} + +void Darwin::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const { + if (isAlignedAllocationUnavailable()) + CC1Args.push_back("-faligned-alloc-unavailable"); +} + DerivedArgList * Darwin::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, Action::OffloadKind DeviceOffloadKind) const { diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index 16ed04286ac0..ffcdf9a71a46 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -384,6 +384,14 @@ protected: return TargetVersion < VersionTuple(V0, V1, V2); } + /// Return true if c++17 aligned allocation/deallocation functions are not + /// implemented in the c++ standard library of the deployment target we are + /// targeting. + bool isAlignedAllocationUnavailable() const; + + void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + StringRef getPlatformFamily() const; static StringRef getSDKName(StringRef isysroot); StringRef getOSLibraryNameSuffix() const; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index c49fda975241..7996da33adce 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2106,6 +2106,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.AlignedAllocation = Args.hasFlag(OPT_faligned_allocation, OPT_fno_aligned_allocation, Opts.AlignedAllocation); + Opts.AlignedAllocationUnavailable = + Opts.AlignedAllocation && Args.hasArg(OPT_aligned_alloc_unavailable); Opts.NewAlignOverride = getLastArgIntValue(Args, OPT_fnew_alignment_EQ, 0, Diags); if (Opts.NewAlignOverride && !llvm::isPowerOf2_32(Opts.NewAlignOverride)) { diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 71c4c8070e77..a9cf3ec7990b 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1646,6 +1646,27 @@ static bool isLegalArrayNewInitializer(CXXNewExpr::InitializationStyle Style, return false; } +// Emit a diagnostic if an aligned allocation/deallocation function that is not +// implemented in the standard library is selected. +static void diagnoseUnavailableAlignedAllocation(const FunctionDecl &FD, + SourceLocation Loc, bool IsDelete, + Sema &S) { + if (!S.getLangOpts().AlignedAllocationUnavailable) + return; + + // Return if there is a definition. + if (FD.isDefined()) + return; + + bool IsAligned = false; + if (FD.isReplaceableGlobalAllocationFunction(&IsAligned) && IsAligned) { + S.Diag(Loc, diag::warn_aligned_allocation_unavailable) + << IsDelete << FD.getType().getAsString() + << S.getASTContext().getTargetInfo().getTriple().str(); + S.Diag(Loc, diag::note_silence_unligned_allocation_unavailable); + } +} + ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, SourceLocation PlacementLParen, @@ -2023,11 +2044,13 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, if (DiagnoseUseOfDecl(OperatorNew, StartLoc)) return ExprError(); MarkFunctionReferenced(StartLoc, OperatorNew); + diagnoseUnavailableAlignedAllocation(*OperatorNew, StartLoc, false, *this); } if (OperatorDelete) { if (DiagnoseUseOfDecl(OperatorDelete, StartLoc)) return ExprError(); MarkFunctionReferenced(StartLoc, OperatorDelete); + diagnoseUnavailableAlignedAllocation(*OperatorDelete, StartLoc, true, *this); } // C++0x [expr.new]p17: @@ -3243,6 +3266,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, PDiag(diag::err_access_dtor) << PointeeElem); } } + + diagnoseUnavailableAlignedAllocation(*OperatorDelete, StartLoc, true, + *this); } CXXDeleteExpr *Result = new (Context) CXXDeleteExpr( diff --git a/clang/test/Driver/unavailable_aligned_allocation.cpp b/clang/test/Driver/unavailable_aligned_allocation.cpp new file mode 100644 index 000000000000..cd75c08165d7 --- /dev/null +++ b/clang/test/Driver/unavailable_aligned_allocation.cpp @@ -0,0 +1,54 @@ +// RUN: %clang -target x86_64-apple-macosx10.12 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// RUN: %clang -target arm64-apple-ios10 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// RUN: %clang -target arm64-apple-tvos10 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// RUN: %clang -target thumbv7-apple-watchos3 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// RUN: %clang -target x86_64-apple-macosx10.13 -mios-simulator-version-min=10 \ +// RUN: -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// RUN: %clang -target x86_64-apple-macosx10.13 -mtvos-simulator-version-min=10 \ +// RUN: -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// RUN: %clang -target x86_64-apple-macosx10.13 -mwatchos-simulator-version-min=3 \ +// RUN: -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=UNAVAILABLE +// +// UNAVAILABLE: "-faligned-alloc-unavailable" + +// RUN: %clang -target x86_64-apple-macosx10.13 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target arm64-apple-ios11 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target arm64-apple-tvos11 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target armv7k-apple-watchos4 -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target x86_64-unknown-linux-gnu -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target x86_64-apple-macosx10.12 -mios-simulator-version-min=11 \ +// RUN: -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target x86_64-apple-macosx10.12 -mtvos-simulator-version-min=11 \ +// RUN: -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// RUN: %clang -target x86_64-apple-macosx10.12 -mwatchos-simulator-version-min=4 \ +// RUN: -c -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=AVAILABLE +// +// AVAILABLE-NOT: "-faligned-alloc-unavailable" diff --git a/clang/test/SemaCXX/unavailable_aligned_allocation.cpp b/clang/test/SemaCXX/unavailable_aligned_allocation.cpp new file mode 100644 index 000000000000..2ae5d2e2c704 --- /dev/null +++ b/clang/test/SemaCXX/unavailable_aligned_allocation.cpp @@ -0,0 +1,109 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12.0 -fexceptions -faligned-alloc-unavailable -std=c++1z -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12.0 -fexceptions -std=c++1z -verify -DNO_ERRORS %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12.0 -fexceptions -faligned-allocation -faligned-alloc-unavailable -std=c++14 -verify %s + +namespace std { + typedef decltype(sizeof(0)) size_t; + enum class align_val_t : std::size_t {}; + struct nothrow_t {}; + nothrow_t nothrow; +} + +void *operator new(std::size_t __sz, const std::nothrow_t&) noexcept; +void *operator new[](std::size_t __sz, const std::nothrow_t&) noexcept; + +void *operator new(std::size_t __sz, std::align_val_t, const std::nothrow_t&) noexcept; +void *operator new[](std::size_t __sz, std::align_val_t, const std::nothrow_t&) noexcept; +void operator delete(void *, std::align_val_t, const std::nothrow_t&); +void operator delete[](void *, std::align_val_t, const std::nothrow_t&); +void operator delete(void*, std::size_t, std::align_val_t) noexcept; +void operator delete[](void*, std::size_t, std::align_val_t) noexcept; + +void *operator new(std::size_t, std::align_val_t, long long); + +struct alignas(256) OveralignedS { + int x[16]; +}; + +struct S { + int x[16]; +}; + +void test() { + auto *p = new S; + delete p; + p = new (std::nothrow) S; + + auto *pa = new S[4]; + delete[] pa; + pa = new (std::nothrow) S[4]; +} + +void testOveraligned() { + auto *p = new OveralignedS; + p = new ((std::align_val_t)8) OveralignedS; + delete p; + p = new (std::nothrow) OveralignedS; + + auto *pa = new OveralignedS[4]; + pa = new ((std::align_val_t)8) OveralignedS[4]; + delete[] pa; + pa = new (std::nothrow) OveralignedS[4]; + // No error here since it is not calling a replaceable allocation function. + p = new ((std::align_val_t)8, 10LL) OveralignedS; +} + +#ifdef NO_ERRORS +// expected-no-diagnostics +#else +// expected-error@-16 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}} +// expected-note@-17 {{if you supply your own aligned allocation functions}} +// expected-error@-18 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}} +// expected-note@-19 {{if you supply your own aligned allocation functions}} + +// expected-error@-20 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}} +// expected-note@-21 {{if you supply your own aligned allocation functions}} +// expected-error@-22 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}} +// expected-note@-23 {{if you supply your own aligned allocation functions}} + +// expected-error@-24 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}} +// expected-note@-25 {{if you supply your own aligned allocation functions}} + +// expected-error@-26 {{aligned allocation function of type 'void *(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}} +// expected-note@-27 {{if you supply your own aligned allocation functions}} +// expected-error@-28 {{aligned deallocation function of type 'void (void *, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}} +// expected-note@-29 {{if you supply your own aligned allocation functions}} + +// expected-error@-29 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}} +// expected-note@-30 {{if you supply your own aligned allocation functions}} +// expected-error@-31 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}} +// expected-note@-32 {{if you supply your own aligned allocation functions}} + +// expected-error@-33 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}} +// expected-note@-34 {{if you supply your own aligned allocation functions}} +// expected-error@-35 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}} +// expected-note@-36 {{if you supply your own aligned allocation functions}} + +// expected-error@-37 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}} +// expected-note@-38 {{if you supply your own aligned allocation functions}} + +// expected-error@-39 {{aligned allocation function of type 'void *(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}} +// expected-note@-40 {{if you supply your own aligned allocation functions}} +// expected-error@-41 {{aligned deallocation function of type 'void (void *, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}} +// expected-note@-42 {{if you supply your own aligned allocation functions}} + +#endif + +// No errors if user-defined aligned allocation functions are available. +void *operator new(std::size_t __sz, std::align_val_t) { + static char array[256]; + return &array; +} + +void operator delete(void *p, std::align_val_t) { +} + +void testOveraligned2() { + auto p = new ((std::align_val_t)8) OveralignedS; + delete p; +}