Explorar el Código

[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

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

Akira Hatanaka hace 8 años
padre
commit
eaa720dff6

+ 4 - 1
include/clang/AST/Decl.h

@@ -2019,7 +2019,10 @@ public:
   /// These functions have special behavior under C++1y [expr.new]:
   /// These functions have special behavior under C++1y [expr.new]:
   ///    An implementation is allowed to omit a call to a replaceable global
   ///    An implementation is allowed to omit a call to a replaceable global
   ///    allocation function. [...]
   ///    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.
   /// Compute the language linkage.
   LanguageLinkage getLanguageLinkage() const;
   LanguageLinkage getLanguageLinkage() const;

+ 1 - 0
include/clang/Basic/DiagnosticGroups.td

@@ -311,6 +311,7 @@ def : DiagGroup<"nonportable-cfstrings">;
 def NonVirtualDtor : DiagGroup<"non-virtual-dtor">;
 def NonVirtualDtor : DiagGroup<"non-virtual-dtor">;
 def : DiagGroup<"effc++", [NonVirtualDtor]>;
 def : DiagGroup<"effc++", [NonVirtualDtor]>;
 def OveralignedType : DiagGroup<"over-aligned">;
 def OveralignedType : DiagGroup<"over-aligned">;
+def AlignedAllocationUnavailable : DiagGroup<"aligned-allocation-unavailable">;
 def OldStyleCast : DiagGroup<"old-style-cast">;
 def OldStyleCast : DiagGroup<"old-style-cast">;
 def : DiagGroup<"old-style-definition">;
 def : DiagGroup<"old-style-definition">;
 def OutOfLineDeclaration : DiagGroup<"out-of-line-declaration">;
 def OutOfLineDeclaration : DiagGroup<"out-of-line-declaration">;

+ 6 - 0
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 "
   "type %0 requires %1 bytes of alignment and the default allocator only "
   "guarantees %2 bytes">,
   "guarantees %2 bytes">,
   InGroup<OveralignedType>, DefaultIgnore;
   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<
 def err_conditional_void_nonvoid : Error<
   "%select{left|right}1 operand to ? is void, but %select{right|left}1 operand "
   "%select{left|right}1 operand to ? is void, but %select{right|left}1 operand "

+ 1 - 0
include/clang/Basic/LangOptions.def

@@ -199,6 +199,7 @@ LANGOPT(CUDADeviceApproxTranscendentals, 1, 0, "using approximate transcendental
 
 
 LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
 LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
 LANGOPT(AlignedAllocation , 1, 0, "aligned allocation")
 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(NewAlignOverride  , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'")
 LANGOPT(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts")
 LANGOPT(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts")
 BENIGN_LANGOPT(ModulesCodegen , 1, 0, "Modules code generation")
 BENIGN_LANGOPT(ModulesCodegen , 1, 0, "Modules code generation")

+ 3 - 0
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">,
 def fno_pch_timestamp : Flag<["-"], "fno-pch-timestamp">,
   HelpText<"Disable inclusion of timestamp in precompiled headers">;
   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
 // Language Options
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//

+ 5 - 2
lib/AST/Decl.cpp

@@ -2630,7 +2630,7 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
   return (proto->getParamType(1).getCanonicalType() == Context.VoidPtrTy);
   return (proto->getParamType(1).getCanonicalType() == Context.VoidPtrTy);
 }
 }
 
 
-bool FunctionDecl::isReplaceableGlobalAllocationFunction() const {
+bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const {
   if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
   if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
     return false;
     return false;
   if (getDeclName().getCXXOverloadedOperator() != OO_New &&
   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
   // In C++17, the next parameter can be a 'std::align_val_t' for aligned
   // new/delete.
   // new/delete.
-  if (Ctx.getLangOpts().AlignedAllocation && !Ty.isNull() && Ty->isAlignValT())
+  if (Ctx.getLangOpts().AlignedAllocation && !Ty.isNull() && Ty->isAlignValT()) {
+    if (IsAligned)
+      *IsAligned = true;
     Consume();
     Consume();
+  }
 
 
   // Finally, if this is not a sized delete, the final parameter can
   // Finally, if this is not a sized delete, the final parameter can
   // be a 'const std::nothrow_t&'.
   // be a 'const std::nothrow_t&'.

+ 21 - 0
lib/Driver/ToolChains/Darwin.cpp

@@ -1667,6 +1667,27 @@ void MachO::AddLinkRuntimeLibArgs(const ArgList &Args,
   AddLinkRuntimeLib(Args, CmdArgs, CompilerRT, false, true);
   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 *
 DerivedArgList *
 Darwin::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
 Darwin::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
                       Action::OffloadKind DeviceOffloadKind) const {
                       Action::OffloadKind DeviceOffloadKind) const {

+ 8 - 0
lib/Driver/ToolChains/Darwin.h

@@ -384,6 +384,14 @@ protected:
     return TargetVersion < VersionTuple(V0, V1, V2);
     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;
   StringRef getPlatformFamily() const;
   static StringRef getSDKName(StringRef isysroot);
   static StringRef getSDKName(StringRef isysroot);
   StringRef getOSLibraryNameSuffix() const;
   StringRef getOSLibraryNameSuffix() const;

+ 2 - 0
lib/Frontend/CompilerInvocation.cpp

@@ -2106,6 +2106,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   Opts.AlignedAllocation =
   Opts.AlignedAllocation =
       Args.hasFlag(OPT_faligned_allocation, OPT_fno_aligned_allocation,
       Args.hasFlag(OPT_faligned_allocation, OPT_fno_aligned_allocation,
                    Opts.AlignedAllocation);
                    Opts.AlignedAllocation);
+  Opts.AlignedAllocationUnavailable =
+      Opts.AlignedAllocation && Args.hasArg(OPT_aligned_alloc_unavailable);
   Opts.NewAlignOverride =
   Opts.NewAlignOverride =
       getLastArgIntValue(Args, OPT_fnew_alignment_EQ, 0, Diags);
       getLastArgIntValue(Args, OPT_fnew_alignment_EQ, 0, Diags);
   if (Opts.NewAlignOverride && !llvm::isPowerOf2_32(Opts.NewAlignOverride)) {
   if (Opts.NewAlignOverride && !llvm::isPowerOf2_32(Opts.NewAlignOverride)) {

+ 26 - 0
lib/Sema/SemaExprCXX.cpp

@@ -1646,6 +1646,27 @@ static bool isLegalArrayNewInitializer(CXXNewExpr::InitializationStyle Style,
   return false;
   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
 ExprResult
 Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
 Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
                   SourceLocation PlacementLParen,
                   SourceLocation PlacementLParen,
@@ -2023,11 +2044,13 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
     if (DiagnoseUseOfDecl(OperatorNew, StartLoc))
     if (DiagnoseUseOfDecl(OperatorNew, StartLoc))
       return ExprError();
       return ExprError();
     MarkFunctionReferenced(StartLoc, OperatorNew);
     MarkFunctionReferenced(StartLoc, OperatorNew);
+    diagnoseUnavailableAlignedAllocation(*OperatorNew, StartLoc, false, *this);
   }
   }
   if (OperatorDelete) {
   if (OperatorDelete) {
     if (DiagnoseUseOfDecl(OperatorDelete, StartLoc))
     if (DiagnoseUseOfDecl(OperatorDelete, StartLoc))
       return ExprError();
       return ExprError();
     MarkFunctionReferenced(StartLoc, OperatorDelete);
     MarkFunctionReferenced(StartLoc, OperatorDelete);
+    diagnoseUnavailableAlignedAllocation(*OperatorDelete, StartLoc, true, *this);
   }
   }
 
 
   // C++0x [expr.new]p17:
   // C++0x [expr.new]p17:
@@ -3243,6 +3266,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
                       PDiag(diag::err_access_dtor) << PointeeElem);
                       PDiag(diag::err_access_dtor) << PointeeElem);
       }
       }
     }
     }
+
+    diagnoseUnavailableAlignedAllocation(*OperatorDelete, StartLoc, true,
+                                         *this);
   }
   }
 
 
   CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(
   CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(

+ 54 - 0
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"

+ 109 - 0
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;
+}