]> granicus.if.org Git - clang/commitdiff
[Sema] Issue diagnostics if a new/delete expression generates a call to
authorAkira Hatanaka <ahatanaka@apple.com>
Thu, 29 Jun 2017 18:48:40 +0000 (18:48 +0000)
committerAkira Hatanaka <ahatanaka@apple.com>
Thu, 29 Jun 2017 18:48:40 +0000 (18:48 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@306722 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/AST/Decl.h
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/LangOptions.def
include/clang/Driver/CC1Options.td
lib/AST/Decl.cpp
lib/Driver/ToolChains/Darwin.cpp
lib/Driver/ToolChains/Darwin.h
lib/Frontend/CompilerInvocation.cpp
lib/Sema/SemaExprCXX.cpp
test/Driver/unavailable_aligned_allocation.cpp [new file with mode: 0644]
test/SemaCXX/unavailable_aligned_allocation.cpp [new file with mode: 0644]

index 30552be9b3811db0c98bf68ca22e1e6d2d480f86..08b34a75aa600f35ef42fa6317f7c5ec2ed37bb7 100644 (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;
index 464c2425a1f1f4a4d008f1a4ee67b23cbbbe7012..3a0564806b32add671658a2945d9536ac8367205 100644 (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">;
index 6224f13ce762d6b26e0fb7827e96b2768ee14539..7b1671bb8777be5b2f14eca849fa37fe5c0eb076 100644 (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 "
index 60c8a68cd2e9246b1a147c0b993cd3fc4520d693..dfdad108922abd2a281b37270524f19f5d2dd5ce 100644 (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")
index c478cfc7833e687f49e11fbe330a3dfc6abe3c60..ab790c8a0a4bf450f2ebf6f9ced427c16a639113 100644 (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
 //===----------------------------------------------------------------------===//
index 9862f4f26473aa6ea585d11023800e5629d5ef91..8677b1155a606174919ea4ec40d022fa1e87db85 100644 (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&'.
index e41b50c40b2898f02ec47f940e3466b90d3051b5..bfe685e70df7bb1a78cdfe0ddd22a9eb1de886e4 100644 (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 {
index 16ed04286ac06bda31cc95e9622b1e2460819cbf..ffcdf9a71a46c92960d6eee6f67cecf08cecc317 100644 (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;
index c49fda975241ddf6801b3aaa7837d986d58ee78e..7996da33adce6ae2f217ce1e8855b16fcc68eda8 100644 (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)) {
index 71c4c8070e778daf9ae592723e4b556f5594f2b1..a9cf3ec7990b29080bb7dcc80df4ac88987855e2 100644 (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(
diff --git a/test/Driver/unavailable_aligned_allocation.cpp b/test/Driver/unavailable_aligned_allocation.cpp
new file mode 100644 (file)
index 0000000..cd75c08
--- /dev/null
@@ -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/test/SemaCXX/unavailable_aligned_allocation.cpp b/test/SemaCXX/unavailable_aligned_allocation.cpp
new file mode 100644 (file)
index 0000000..2ae5d2e
--- /dev/null
@@ -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;
+}