[compiler-rt] a633a37 - Diagnose problematic uses of constructor/destructor attribute (#67673)

via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 11 05:14:51 PDT 2023


Author: Aaron Ballman
Date: 2023-10-11T08:14:45-04:00
New Revision: a633a3761fcbd0799426cbf5fbd7794961080e43

URL: https://github.com/llvm/llvm-project/commit/a633a3761fcbd0799426cbf5fbd7794961080e43
DIFF: https://github.com/llvm/llvm-project/commit/a633a3761fcbd0799426cbf5fbd7794961080e43.diff

LOG: Diagnose problematic uses of constructor/destructor attribute (#67673)

Functions with these attributes will be automatically called before
main() or after main() exits gracefully. In glibc environments, the
constructor function is passed the same arguments as main(), so that
signature is allowed. In all other environments, we require the function
to accept no arguments and either return `void` or `int`. The functions
must use the C calling convention. In C++ language modes, the functions
cannot be a nonstatic member function, or a consteval function.

Additionally, these reuse the same priority logic as the init_priority
attribute which explicitly reserved priorty values <= 100 or > 65535. So
we now diagnose use of reserved priorities the same as we do for the
init_priority attribute, but we downgrade the error to be a warning
which defaults to an error to ease use for implementers like compiler-rt
or libc.

This relands b4435104ca3904529723b0673cc0f624cf8c54e6 with fixes.

Added: 
    clang/test/Sema/constructor-attribute-diag-group.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/AttrDocs.td
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/test/CodeGen/PowerPC/aix-destructor-attribute.c
    clang/test/CodeGenCXX/aix-destructor-attribute.cpp
    clang/test/Sema/constructor-attribute.c
    compiler-rt/CMakeLists.txt
    compiler-rt/cmake/config-ix.cmake
    compiler-rt/test/profile/Posix/gcov-destructor.c
    compiler-rt/test/ubsan/TestCases/Misc/Linux/sigaction.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2d918967e7f0b02..530dbe55fcb1362 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -217,6 +217,11 @@ Attribute Changes in Clang
   automatic diagnostic to use parameters of types that the format style
   supports but that are never the result of default argument promotion, such as
   ``float``. (`#59824: <https://github.com/llvm/llvm-project/issues/59824>`_)
+- The ``constructor`` and ``destructor`` attributes now diagnose when:
+  - the priority is not between 101 and 65535, inclusive,
+  - the function it is applied to accepts arguments or has a non-void return
+    type, or
+  - the function it is applied to is a non-static member function (C++).
 
 Improvements to Clang's diagnostics
 -----------------------------------

diff  --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 8d928dcc146b254..eb4ccc6fb9389d4 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7255,8 +7255,14 @@ after returning from ``main()`` or when the ``exit()`` function has been
 called. Note, ``quick_exit()``, ``_Exit()``, and ``abort()`` prevent a function
 marked ``destructor`` from being called.
 
-The constructor or destructor function should not accept any arguments and its
-return type should be ``void``.
+In general, the constructor or destructor function must use the C calling
+convention, cannot accept any arguments, and its return type should be
+``void``, ``int``, or ``unsigned int``. The latter two types are supported for
+historical reasons. On targets with a GNU environment (one which uses glibc),
+the signature of the function can also be the same as that of ``main()``.
+
+In C++ language modes, the function cannot be marked ``consteval``, nor can it
+be a non-static member function.
 
 The attributes accept an optional argument used to specify the priority order
 in which to execute constructor and destructor functions. The priority is

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 0b09c002191848a..e017ca45aeeeb67 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -105,6 +105,10 @@ def EnumConversion : DiagGroup<"enum-conversion",
                                [EnumEnumConversion,
                                 EnumFloatConversion,
                                 EnumCompareConditional]>;
+def InvalidPriority : DiagGroup<"priority-ctor-dtor">;
+// For compatibility with GCC.
+def : DiagGroup<"prio-ctor-dtor", [InvalidPriority]>;
+
 def ObjCSignedCharBoolImplicitIntConversion :
   DiagGroup<"objc-signed-char-bool-implicit-int-conversion">;
 def ImplicitIntConversion : DiagGroup<"implicit-int-conversion",

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c1a6e3831127e56..cce4601770a7ecc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2870,6 +2870,8 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning<
   InGroup<CXXPre14Compat>, DefaultIgnore;
 def note_constexpr_body_previous_return : Note<
   "previous return statement is here">;
+def err_ctordtor_attr_consteval : Error<
+  "%0 attribute cannot be applied to a 'consteval' function">;
 
 // C++20 function try blocks in constexpr
 def ext_constexpr_function_try_block_cxx20 : ExtWarn<
@@ -3182,6 +3184,13 @@ def err_alignas_underaligned : Error<
   "requested alignment is less than minimum alignment of %1 for type %0">;
 def warn_aligned_attr_underaligned : Warning<err_alignas_underaligned.Summary>,
   InGroup<IgnoredAttributes>;
+def err_ctor_dtor_attr_on_non_void_func : Error<
+  "%0 attribute can only be applied to a function which accepts no arguments "
+  "and has a 'void' or 'int' return type">;
+def err_ctor_dtor_member_func : Error<
+  "%0 attribute cannot be applied to a member function">;
+def err_ctor_dtor_calling_conv : Error<
+  "%0 attribute must be applied to a function with the C calling convention">;
 def err_attribute_sizeless_type : Error<
   "%0 attribute cannot be applied to sizeless type %1">;
 def err_attribute_argument_n_type : Error<
@@ -3195,6 +3204,9 @@ def err_attribute_argument_out_of_range : Error<
 def err_init_priority_object_attr : Error<
   "can only use 'init_priority' attribute on file-scope definitions "
   "of objects of class type">;
+def warn_priority_out_of_range : Warning<
+  err_attribute_argument_out_of_range.Summary>,
+  InGroup<InvalidPriority>, DefaultError;
 def err_attribute_argument_out_of_bounds : Error<
   "%0 attribute parameter %1 is out of bounds">;
 def err_attribute_only_once_per_parameter : Error<

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ed0b4d29b056397..964fcd6c6b15f5d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2352,26 +2352,126 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL));
 }
 
-static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
-  uint32_t priority = ConstructorAttr::DefaultPriority;
+static void diagnoseInvalidPriority(Sema &S, uint32_t Priority,
+                                    const ParsedAttr &A,
+                                    SourceLocation PriorityLoc) {
+  constexpr uint32_t ReservedPriorityLower = 101, ReservedPriorityUpper = 65535;
+
+  // Only perform the priority check if the attribute is outside of a system
+  // header. Values <= 100 are reserved for the implementation, and libc++
+  // benefits from being able to specify values in that range. Values > 65535
+  // are reserved for historical reasons.
+  if ((Priority < ReservedPriorityLower || Priority > ReservedPriorityUpper) &&
+      !S.getSourceManager().isInSystemHeader(A.getLoc())) {
+    S.Diag(A.getLoc(), diag::warn_priority_out_of_range)
+        << PriorityLoc << A << ReservedPriorityLower << ReservedPriorityUpper;
+  }
+}
+
+static bool FunctionParamsAreMainLike(ASTContext &Context,
+                                      const FunctionDecl *FD) {
+  assert(FD->hasPrototype() && "expected the function to have a prototype");
+  const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
+  QualType CharPP =
+      Context.getPointerType(Context.getPointerType(Context.CharTy));
+  QualType Expected[] = {Context.IntTy, CharPP, CharPP, CharPP};
+  for (unsigned I = 0;
+       I < sizeof(Expected) / sizeof(QualType) && I < FPT->getNumParams();
+       ++I) {
+    QualType AT = FPT->getParamType(I);
+
+    if (!Context.hasSameUnqualifiedType(AT, Expected[I])) {
+      if (Expected[I] == CharPP) {
+        // As an extension, the following forms are okay:
+        //   char const **
+        //   char const * const *
+        //   char * const *
+
+        QualifierCollector Qs;
+        const PointerType *PT;
+        if ((PT = Qs.strip(AT)->getAs<PointerType>()) &&
+            (PT = Qs.strip(PT->getPointeeType())->getAs<PointerType>()) &&
+            Context.hasSameType(QualType(Qs.strip(PT->getPointeeType()), 0),
+                                Context.CharTy)) {
+          Qs.removeConst();
+          if (!Qs.empty())
+            return false;
+          continue; // Accepted as an extension.
+        }
+      }
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename CtorDtorAttr>
+static void handleCtorDtorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  uint32_t Priority = CtorDtorAttr::DefaultPriority;
   if (S.getLangOpts().HLSL && AL.getNumArgs()) {
     S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
     return;
   }
-  if (AL.getNumArgs() &&
-      !checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority))
-    return;
 
-  D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority));
-}
+  // If we're given an argument for the priority, check that it's valid.
+  if (AL.getNumArgs()) {
+    if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), Priority))
+      return;
+
+    // Diagnose an invalid priority, but continue to process the attribute.
+    diagnoseInvalidPriority(S, Priority, AL, AL.getArgAsExpr(0)->getExprLoc());
+  }
 
-static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
-  uint32_t priority = DestructorAttr::DefaultPriority;
-  if (AL.getNumArgs() &&
-      !checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority))
+  // Ensure the function we're attaching to is something that is sensible to
+  // automatically call before or after main(); it should accept no arguments.
+  // In theory, a void return type is the only truly safe return type (consider
+  // that calling conventions may place returned values in a hidden pointer
+  // argument passed to the function that will not be present when called
+  // automatically). However, there is a significant amount of existing code
+  // which uses an int return type. So we will accept void, int, and
+  // unsigned int return types. Any other return type, or a non-void parameter
+  // list is treated as an error because it's a form of type system
+  // incompatibility. The function also cannot be a member function. We allow
+  // K&R C functions because that's a 
diff icult edge case where it depends on
+  // how the function is defined as to whether it does or does not expect
+  // arguments.
+  //
+  // However! glibc on ELF will pass the same arguments to a constructor
+  // function as are given to main(), so we will allow `int, char *[]` and
+  // `int, char *[], char *[]` (or qualified versions thereof), but only if
+  // the target is explicitly for glibc.
+  const auto *FD = cast<FunctionDecl>(D);
+  QualType RetTy = FD->getReturnType();
+  bool IsGlibC = S.Context.getTargetInfo().getTriple().isGNUEnvironment();
+  if (!(RetTy->isVoidType() ||
+        RetTy->isSpecificBuiltinType(BuiltinType::UInt) ||
+        RetTy->isSpecificBuiltinType(BuiltinType::Int)) ||
+      FD->isVariadic() ||
+      (FD->hasPrototype() &&
+       ((!IsGlibC && FD->getNumParams() != 0) ||
+        (IsGlibC && !FunctionParamsAreMainLike(S.Context, FD))))) {
+    S.Diag(AL.getLoc(), diag::err_ctor_dtor_attr_on_non_void_func)
+        << AL << FD->getSourceRange();
+    return;
+  }
+  if (FD->getType()->castAs<FunctionType>()->getCallConv() !=
+      CallingConv::CC_C) {
+    S.Diag(AL.getLoc(), diag::err_ctor_dtor_calling_conv)
+        << AL << FD->getSourceRange();
+    return;
+  }
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); MD && MD->isInstance()) {
+    S.Diag(AL.getLoc(), diag::err_ctor_dtor_member_func)
+        << AL << FD->getSourceRange();
+    return;
+  }
+  if (FD->isConsteval()) {
+    S.Diag(AL.getLoc(), diag::err_ctordtor_attr_consteval)
+        << AL << FD->getSourceRange();
     return;
+  }
 
-  D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority));
+  D->addAttr(CtorDtorAttr::Create(S.Context, Priority, AL));
 }
 
 template <typename AttrTy>
@@ -3888,16 +3988,9 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  // Only perform the priority check if the attribute is outside of a system
-  // header. Values <= 100 are reserved for the implementation, and libc++
-  // benefits from being able to specify values in that range.
-  if ((prioritynum < 101 || prioritynum > 65535) &&
-      !S.getSourceManager().isInSystemHeader(AL.getLoc())) {
-    S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_range)
-        << E->getSourceRange() << AL << 101 << 65535;
-    AL.setInvalid();
-    return;
-  }
+  // Diagnose an invalid priority, but continue to process the attribute.
+  diagnoseInvalidPriority(S, prioritynum, AL, E->getExprLoc());
+
   D->addAttr(::new (S.Context) InitPriorityAttr(S.Context, AL, prioritynum));
 }
 
@@ -8959,13 +9052,13 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
     handlePassObjectSizeAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Constructor:
-      handleConstructorAttr(S, D, AL);
+    handleCtorDtorAttr<ConstructorAttr>(S, D, AL);
     break;
   case ParsedAttr::AT_Deprecated:
     handleDeprecatedAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Destructor:
-      handleDestructorAttr(S, D, AL);
+    handleCtorDtorAttr<DestructorAttr>(S, D, AL);
     break;
   case ParsedAttr::AT_EnableIf:
     handleEnableIfAttr(S, D, AL);

diff  --git a/clang/test/CodeGen/PowerPC/aix-destructor-attribute.c b/clang/test/CodeGen/PowerPC/aix-destructor-attribute.c
index cfb1fd7b0171a41..f0d83d161be50bd 100644
--- a/clang/test/CodeGen/PowerPC/aix-destructor-attribute.c
+++ b/clang/test/CodeGen/PowerPC/aix-destructor-attribute.c
@@ -12,9 +12,8 @@
 // RUN:     -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \
 // RUN:   FileCheck --check-prefix=REGISTER %s
 
-int bar(void) __attribute__((destructor(100)));
+int bar(void) __attribute__((destructor(101)));
 int bar2(void) __attribute__((destructor(65535)));
-int bar3(int) __attribute__((destructor(65535)));
 
 int bar(void) {
   return 1;
@@ -24,16 +23,12 @@ int bar2(void) {
   return 2;
 }
 
-int bar3(int a) {
-  return a;
-}
-
-// NO-REGISTER: @llvm.global_dtors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 100, ptr @bar, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @bar2, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @bar3, ptr null }]
+// NO-REGISTER: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @bar, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @bar2, ptr null }]
 
-// REGISTER: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 100, ptr @__GLOBAL_init_100, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_init_65535, ptr null }]
-// REGISTER: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 100, ptr @__GLOBAL_cleanup_100, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_cleanup_65535, ptr null }]
+// REGISTER: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @__GLOBAL_init_101, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_init_65535, ptr null }]
+// REGISTER: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @__GLOBAL_cleanup_101, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_cleanup_65535, ptr null }]
 
-// REGISTER: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] {
+// REGISTER: define internal void @__GLOBAL_init_101() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
 // REGISTER:   %0 = call i32 @atexit(ptr @bar)
 // REGISTER:   ret void
@@ -42,11 +37,10 @@ int bar3(int a) {
 // REGISTER: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
 // REGISTER:   %0 = call i32 @atexit(ptr @bar2)
-// REGISTER:   %1 = call i32 @atexit(ptr @bar3)
 // REGISTER:   ret void
 // REGISTER: }
 
-// REGISTER: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] {
+// REGISTER: define internal void @__GLOBAL_cleanup_101() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
 // REGISTER:   %0 = call i32 @unatexit(ptr @bar)
 // REGISTER:   %needs_destruct = icmp eq i32 %0, 0
@@ -62,20 +56,11 @@ int bar3(int a) {
 
 // REGISTER: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
-// REGISTER:   %0 = call i32 @unatexit(ptr @bar3)
+// REGISTER:   %0 = call i32 @unatexit(ptr @bar2)
 // REGISTER:   %needs_destruct = icmp eq i32 %0, 0
-// REGISTER:   br i1 %needs_destruct, label %destruct.call, label %unatexit.call
+// REGISTER:   br i1 %needs_destruct, label %destruct.call, label %destruct.end
 
 // REGISTER: destruct.call:
-// REGISTER:   call void @bar3()
-// REGISTER:   br label %unatexit.call
-
-// REGISTER: unatexit.call:
-// REGISTER:   %1 = call i32 @unatexit(ptr @bar2)
-// REGISTER:   %needs_destruct1 = icmp eq i32 %1, 0
-// REGISTER:   br i1 %needs_destruct1, label %destruct.call2, label %destruct.end
-
-// REGISTER: destruct.call2:
 // REGISTER:   call void @bar2()
 // REGISTER:   br label %destruct.end
 

diff  --git a/clang/test/CodeGenCXX/aix-destructor-attribute.cpp b/clang/test/CodeGenCXX/aix-destructor-attribute.cpp
index 5ebea7a997c9db1..2cdf147af8d07f1 100644
--- a/clang/test/CodeGenCXX/aix-destructor-attribute.cpp
+++ b/clang/test/CodeGenCXX/aix-destructor-attribute.cpp
@@ -17,9 +17,8 @@ struct test {
   ~test();
 } t;
 
-int bar() __attribute__((destructor(100)));
+int bar() __attribute__((destructor(101)));
 int bar2() __attribute__((destructor(65535)));
-int bar3(int) __attribute__((destructor(65535)));
 
 int bar() {
   return 1;
@@ -29,17 +28,13 @@ int bar2() {
   return 2;
 }
 
-int bar3(int a) {
-  return a;
-}
-
 // NO-REGISTER: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I__, ptr null }]
-// NO-REGISTER: @llvm.global_dtors = appending global [4 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 100, ptr @_Z3barv, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @_Z4bar2v, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @_Z4bar3i, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__D_a, ptr null }]
+// NO-REGISTER: @llvm.global_dtors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @_Z3barv, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @_Z4bar2v, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__D_a, ptr null }]
 
-// REGISTER: @llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I__, ptr null }, { i32, ptr, ptr } { i32 100, ptr @__GLOBAL_init_100, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_init_65535, ptr null }]
-// REGISTER: @llvm.global_dtors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__D_a, ptr null }, { i32, ptr, ptr } { i32 100, ptr @__GLOBAL_cleanup_100, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_cleanup_65535, ptr null }]
+// REGISTER: @llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I__, ptr null }, { i32, ptr, ptr } { i32 101, ptr @__GLOBAL_init_101, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_init_65535, ptr null }]
+// REGISTER: @llvm.global_dtors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__D_a, ptr null }, { i32, ptr, ptr } { i32 101, ptr @__GLOBAL_cleanup_101, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @__GLOBAL_cleanup_65535, ptr null }]
 
-// REGISTER: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] {
+// REGISTER: define internal void @__GLOBAL_init_101() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
 // REGISTER:   %0 = call i32 @atexit(ptr @_Z3barv)
 // REGISTER:   ret void
@@ -48,11 +43,10 @@ int bar3(int a) {
 // REGISTER: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
 // REGISTER:   %0 = call i32 @atexit(ptr @_Z4bar2v)
-// REGISTER:   %1 = call i32 @atexit(ptr @_Z4bar3i)
 // REGISTER:   ret void
 // REGISTER: }
 
-// REGISTER: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] {
+// REGISTER: define internal void @__GLOBAL_cleanup_101() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
 // REGISTER:   %0 = call i32 @unatexit(ptr @_Z3barv)
 // REGISTER:   %needs_destruct = icmp eq i32 %0, 0
@@ -68,20 +62,11 @@ int bar3(int a) {
 
 // REGISTER: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] {
 // REGISTER: entry:
-// REGISTER:   %0 = call i32 @unatexit(ptr @_Z4bar3i)
+// REGISTER:   %0 = call i32 @unatexit(ptr @_Z4bar2v)
 // REGISTER:   %needs_destruct = icmp eq i32 %0, 0
-// REGISTER:   br i1 %needs_destruct, label %destruct.call, label %unatexit.call
+// REGISTER:   br i1 %needs_destruct, label %destruct.call, label %destruct.end
 
 // REGISTER: destruct.call:
-// REGISTER:   call void @_Z4bar3i()
-// REGISTER:   br label %unatexit.call
-
-// REGISTER: unatexit.call:
-// REGISTER:   %1 = call i32 @unatexit(ptr @_Z4bar2v)
-// REGISTER:   %needs_destruct1 = icmp eq i32 %1, 0
-// REGISTER:   br i1 %needs_destruct1, label %destruct.call2, label %destruct.end
-
-// REGISTER: destruct.call2:
 // REGISTER:   call void @_Z4bar2v()
 // REGISTER:   br label %destruct.end
 

diff  --git a/clang/test/Sema/constructor-attribute-diag-group.c b/clang/test/Sema/constructor-attribute-diag-group.c
new file mode 100644
index 000000000000000..e1828d62d28b754
--- /dev/null
+++ b/clang/test/Sema/constructor-attribute-diag-group.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=err %s
+// RUN: %clang_cc1 -fsyntax-only -verify=warn -Wno-error=priority-ctor-dtor %s
+// RUN: %clang_cc1 -fsyntax-only -verify=okay -Wno-priority-ctor-dtor %s
+// RUN: %clang_cc1 -fsyntax-only -verify=okay -Wno-prio-ctor-dtor %s
+// okay-no-diagnostics
+
+void f(void) __attribute__((constructor(1)));   // warn-warning {{'constructor' attribute requires integer constant between 101 and 65535 inclusive}} \
+                                                   err-error {{'constructor' attribute requires integer constant between 101 and 65535 inclusive}}
+void f(void) __attribute__((destructor(1)));    // warn-warning {{'destructor' attribute requires integer constant between 101 and 65535 inclusive}} \
+                                                   err-error {{'destructor' attribute requires integer constant between 101 and 65535 inclusive}}

diff  --git a/clang/test/Sema/constructor-attribute.c b/clang/test/Sema/constructor-attribute.c
index 2317c7735bda586..d29be13f68f89db 100644
--- a/clang/test/Sema/constructor-attribute.c
+++ b/clang/test/Sema/constructor-attribute.c
@@ -1,16 +1,75 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-strict-prototypes %s
-
-int x __attribute__((constructor)); // expected-warning {{'constructor' attribute only applies to functions}}
-int f(void) __attribute__((constructor));
-int f(void) __attribute__((constructor(1)));
-int f(void) __attribute__((constructor(1,2))); // expected-error {{'constructor' attribute takes no more than 1 argument}}
-int f(void) __attribute__((constructor(1.0))); // expected-error {{'constructor' attribute requires an integer constant}}
-int f(void) __attribute__((constructor(0x100000000))); // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
-
-int x __attribute__((destructor)); // expected-warning {{'destructor' attribute only applies to functions}}
-int f(void) __attribute__((destructor));
-int f(void) __attribute__((destructor(1)));
-int f(void) __attribute__((destructor(1,2))); // expected-error {{'destructor' attribute takes no more than 1 argument}}
-int f(void) __attribute__((destructor(1.0))); // expected-error {{'destructor' attribute requires an integer constant}}
-
-void knr() __attribute__((constructor));
+// RUN: %clang_cc1 -triple x86_64-pc-linux-musl -fsyntax-only -verify=expected,nonglibc -Wno-strict-prototypes %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -verify -Wno-strict-prototypes %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-musl -fsyntax-only -verify=expected,nonglibc -x c++ -std=c++20 %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -verify -x c++ -std=c++20 %s
+
+int x1 __attribute__((constructor)); // expected-warning {{'constructor' attribute only applies to functions}}
+void f(void) __attribute__((constructor));
+void f(void) __attribute__((constructor(1)));   // expected-error {{'constructor' attribute requires integer constant between 101 and 65535 inclusive}}
+void f(void) __attribute__((constructor(1,2))); // expected-error {{'constructor' attribute takes no more than 1 argument}}
+void f(void) __attribute__((constructor(1.0))); // expected-error {{'constructor' attribute requires an integer constant}}
+void f(void) __attribute__((constructor(0x100000000))); // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
+void f(void) __attribute__((constructor(101)));
+
+int x2 __attribute__((destructor)); // expected-warning {{'destructor' attribute only applies to functions}}
+void f(void) __attribute__((destructor));
+void f(void) __attribute__((destructor(1)));   // expected-error {{'destructor' attribute requires integer constant between 101 and 65535 inclusive}}
+void f(void) __attribute__((destructor(1,2))); // expected-error {{'destructor' attribute takes no more than 1 argument}}
+void f(void) __attribute__((destructor(1.0))); // expected-error {{'destructor' attribute requires an integer constant}}
+void f(void) __attribute__((destructor(101)));
+
+void knr1() __attribute__((constructor));
+void knr2() __attribute__((destructor));
+
+// Require a void or (unsigned) int return type
+int g0(void) __attribute__((constructor));
+signed int g1(void) __attribute__((constructor));
+float g2(void) __attribute__((constructor)); // expected-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+int h0(void) __attribute__((destructor));
+unsigned int h1(void) __attribute__((destructor));
+float h2(void) __attribute__((destructor));  // expected-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+
+// In glibc environments, allow main-like signatures, but otherwise disallow
+// any parameters.
+void i1(int v) __attribute__((constructor)); // nonglibc-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void j1(int v) __attribute__((destructor));  // nonglibc-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void i2(int argc, char *argv[]) __attribute__((constructor)); // nonglibc-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void j2(int argc, char *argv[]) __attribute__((destructor));  // nonglibc-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void i3(int argc, char *const argv[], char *environ[]) __attribute__((constructor)); // nonglibc-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void j3(int argc, const char *argv[], char *environ[]) __attribute__((destructor));  // nonglibc-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void i4(int argc, float f) __attribute__((constructor)); // expected-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+void j4(int argc, float f) __attribute__((destructor));  // expected-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+
+// Disallow calling conventions other than the C calling convention
+__attribute__((regcall, constructor)) void k(void); // expected-error {{'constructor' attribute must be applied to a function with the C calling convention}}
+
+#ifdef __cplusplus
+// Disallow variadic functions.
+__attribute__((constructor)) void g1(...); // expected-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+__attribute__((destructor)) void g2(...);  // expected-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+
+struct S {
+  // Not allowed on a nonstatic member function, but is allowed on a static
+  // member function so long as it has no args/void return type.
+  void mem1() __attribute__((constructor)); // expected-error {{'constructor' attribute cannot be applied to a member function}}
+  void mem2() __attribute__((destructor));  // expected-error {{'destructor' attribute cannot be applied to a member function}}
+
+  static signed nonmem1() __attribute__((constructor));
+  static unsigned nonmem2() __attribute__((destructor));
+
+  static _BitInt(32) nonmem3() __attribute__((constructor)); // expected-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+  static char nonmem4() __attribute__((destructor));         // expected-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+
+  static void nonmem5(int) __attribute__((constructor)); // nonglibc-error {{'constructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+  static void nonmem6(int) __attribute__((destructor));  // nonglibc-error {{'destructor' attribute can only be applied to a function which accepts no arguments and has a 'void' or 'int' return type}}
+};
+
+consteval void consteval_func1() __attribute__((constructor)); // expected-error {{'constructor' attribute cannot be applied to a 'consteval' function}}
+consteval void consteval_func2() __attribute__((destructor));  // expected-error {{'destructor' attribute cannot be applied to a 'consteval' function}}
+#endif // __cplusplus
+
+# 1 "source.c" 1 3
+// Can use reserved priorities within a system header
+void f(void) __attribute__((constructor(1)));
+void f(void) __attribute__((destructor(1)));
+# 1 "source.c" 2

diff  --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index 1a46f5b33480694..2e4b2c7070bb91f 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -480,6 +480,7 @@ endif()
 append_list_if(COMPILER_RT_HAS_WGNU_FLAG -Wno-gnu SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG -Wno-variadic-macros SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_WC99_EXTENSIONS_FLAG -Wno-c99-extensions SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WPRIO_CTOR_DTOR_FLAG -Wno-prio-ctor-dtor SANITIZER_COMMON_CFLAGS)
 # format-pedantic warns about passing T* for %p, which is not useful.
 append_list_if(COMPILER_RT_HAS_WD4146_FLAG /wd4146 SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_WD4291_FLAG /wd4291 SANITIZER_COMMON_CFLAGS)

diff  --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
index a8e078f1ebc9888..9ec08f8e99ea2fe 100644
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -137,7 +137,7 @@ check_cxx_compiler_flag("-Werror -Wthread-safety-beta" COMPILER_RT_HAS_WTHREAD_S
 check_cxx_compiler_flag(-Wno-pedantic COMPILER_RT_HAS_WNO_PEDANTIC)
 check_cxx_compiler_flag(-Wno-format COMPILER_RT_HAS_WNO_FORMAT)
 check_cxx_compiler_flag(-Wno-format-pedantic COMPILER_RT_HAS_WNO_FORMAT_PEDANTIC)
-
+check_cxx_compiler_flag(-Wno-prio-ctor-dtor COMPILER_RT_HAS_WPRIO_CTOR_DTOR_FLAG)
 check_cxx_compiler_flag("/experimental:external /external:W0" COMPILER_RT_HAS_EXTERNAL_FLAG)
 
 check_cxx_compiler_flag(/W4 COMPILER_RT_HAS_W4_FLAG)

diff  --git a/compiler-rt/test/profile/Posix/gcov-destructor.c b/compiler-rt/test/profile/Posix/gcov-destructor.c
index bd1e0d2dde079b2..ed4360636c1fcdb 100644
--- a/compiler-rt/test/profile/Posix/gcov-destructor.c
+++ b/compiler-rt/test/profile/Posix/gcov-destructor.c
@@ -1,6 +1,6 @@
 /// Test that destructors and destructors whose priorities are greater than 100 are tracked.
 // RUN: mkdir -p %t.dir && cd %t.dir
-// RUN: %clang --coverage %s -o %t -dumpdir ./
+// RUN: %clang -Wno-prio-ctor-dtor --coverage %s -o %t -dumpdir ./
 // RUN: rm -f gcov-destructor.gcda && %run %t
 // RUN: llvm-cov gcov -t gcov-destructor.gcda | FileCheck %s
 // UNSUPPORTED: darwin

diff  --git a/compiler-rt/test/ubsan/TestCases/Misc/Linux/sigaction.cpp b/compiler-rt/test/ubsan/TestCases/Misc/Linux/sigaction.cpp
index 0ab65bd30d92cc8..f89c14acca98de1 100644
--- a/compiler-rt/test/ubsan/TestCases/Misc/Linux/sigaction.cpp
+++ b/compiler-rt/test/ubsan/TestCases/Misc/Linux/sigaction.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=undefined -shared-libsan %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx -fsanitize=undefined -Wno-prio-ctor-dtor -shared-libsan %s -o %t && %run %t 2>&1 | FileCheck %s
 
 // Ensure ubsan runtime/interceptors are lazily initialized if called early.
 


        


More information about the llvm-commits mailing list