[clang] b443510 - Diagnose problematic uses of constructor/destructor attribute (#67360)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 26 09:54:35 PDT 2023
Author: Aaron Ballman
Date: 2023-09-26T12:54:30-04:00
New Revision: b4435104ca3904529723b0673cc0f624cf8c54e6
URL: https://github.com/llvm/llvm-project/commit/b4435104ca3904529723b0673cc0f624cf8c54e6
DIFF: https://github.com/llvm/llvm-project/commit/b4435104ca3904529723b0673cc0f624cf8c54e6.diff
LOG: Diagnose problematic uses of constructor/destructor attribute (#67360)
Functions with these attributes will be automatically called before
`main()` or after `main()` exits gracefully, which means the functions
should not accept arguments or have a returned value (nothing can
provide an argument to the call in these cases, and nothing can use the
returned value), nor should they be allowed on a non-static member
function or consteval function in C++. We allow 'int' as a return type for
the function due to finding a significant amount of historical code using
`int(void)` as a signature.
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.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/AttrDocs.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
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8a136aae5489a8c..bdf2f47e3714419 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -171,6 +171,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 2f9d4d1b7907b20..7720580b2c34912 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7251,8 +7251,10 @@ 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``.
+The constructor or destructor function cannot accept any arguments and its
+return type should be ``void``, ``int``, or ``unsigned int``. The latter two
+types are supported for historical reasons. 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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f4eb02fd9570c2f..3f4624d1d09db6f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2864,6 +2864,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<
@@ -3176,6 +3178,11 @@ 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_attribute_sizeless_type : Error<
"%0 attribute cannot be applied to sizeless type %1">;
def err_attribute_argument_n_type : Error<
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 090a54eedaa07d0..a0000a2118e812a 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2352,26 +2352,78 @@ 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 bool 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::err_attribute_argument_out_of_range)
+ << PriorityLoc << A << ReservedPriorityLower << ReservedPriorityUpper;
+ A.setInvalid();
+ return true;
+ }
+ return false;
+}
+
+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;
-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 priority is in a reasonable range.
+ if (diagnoseInvalidPriority(S, Priority, AL,
+ AL.getArgAsExpr(0)->getExprLoc()))
+ return;
+ }
+
+ // 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.
+ const auto *FD = cast<FunctionDecl>(D);
+ QualType RetTy = FD->getReturnType();
+ if (!(RetTy->isVoidType() ||
+ RetTy->isSpecificBuiltinType(BuiltinType::UInt) ||
+ RetTy->isSpecificBuiltinType(BuiltinType::Int)) ||
+ (FD->hasPrototype() && FD->getNumParams() != 0)) {
+ S.Diag(AL.getLoc(), diag::err_ctor_dtor_attr_on_non_void_func)
+ << AL << FD->getSourceRange();
return;
+ } else 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;
+ } else 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 +3940,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();
+ if (diagnoseInvalidPriority(S, prioritynum, AL, E->getExprLoc()))
return;
- }
+
D->addAttr(::new (S.Context) InitPriorityAttr(S.Context, AL, prioritynum));
}
@@ -8930,13 +8975,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.c b/clang/test/Sema/constructor-attribute.c
index 2317c7735bda586..4d91f84af103af2 100644
--- a/clang/test/Sema/constructor-attribute.c
+++ b/clang/test/Sema/constructor-attribute.c
@@ -1,16 +1,59 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-strict-prototypes %s
+// RUN: %clang_cc1 -fsyntax-only -verify -x c++ -std=c++20 %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 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 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}}
+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 knr() __attribute__((constructor));
+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}}
+
+// Require no parameters
+void i(int v) __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 j(int v) __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}}
+
+#ifdef __cplusplus
+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)); // expected-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)); // expected-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
More information about the cfe-commits
mailing list