[Sema] Issue diagnostics if a new/delete expression generates a call to

a c++17 aligned allocation/deallocation function that is unavailable in
the standard library on Apple platforms.

The aligned functions are implemented only in the following versions or
later versions of the OSes, so clang issues diagnostics if the deployment
target being targeted is older than these:

macosx: 10.13
ios: 11.0
tvos: 11.0
watchos: 4.0

The diagnostics are issued whenever the aligned functions are selected
except when the selected function has a definition in the same file.
If there is a user-defined function available somewhere else, option
-Wno-aligned-allocation-unavailable can be used to silence the
diagnostics.

rdar://problem/32664169

Differential Revision: https://reviews.llvm.org/D34574

llvm-svn: 306722
This commit is contained in:
Akira Hatanaka 2017-06-29 18:48:40 +00:00
parent e9e5c4f975
commit cae83f78aa
12 changed files with 240 additions and 3 deletions

View File

@ -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;

View File

@ -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">;

View File

@ -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<OveralignedType>, DefaultIgnore;
def warn_aligned_allocation_unavailable :Warning<
"aligned %select{allocation|deallocation}0 function of type '%1' possibly "
"unavailable on %2">, InGroup<AlignedAllocationUnavailable>, 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 "

View File

@ -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")

View File

@ -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
//===----------------------------------------------------------------------===//

View File

@ -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&'.

View File

@ -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 {

View File

@ -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;

View File

@ -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)) {

View File

@ -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(

View File

@ -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"

View File

@ -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;
}