[clang] [Clang] Add template argument support for {con,de}structor attributes. (PR #151400)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 15 09:32:03 PDT 2025
https://github.com/tynasello updated https://github.com/llvm/llvm-project/pull/151400
>From e7c6ad6a7642c90414da78073c29695fe5ea036f Mon Sep 17 00:00:00 2001
From: tynasello <tynasello at gmail.com>
Date: Tue, 29 Jul 2025 23:41:09 +0000
Subject: [PATCH 1/2] [Clang] Add template argument support for
{con,de}structor attributes.
Fixes: https://github.com/llvm/llvm-project/issues/67154
---
clang/include/clang/Basic/Attr.td | 12 +++++-
clang/lib/CodeGen/CodeGenModule.cpp | 14 ++++++-
clang/lib/Sema/SemaDeclAttr.cpp | 39 +++++++++++++------
clang/test/AST/ast-dump-attr.cpp | 3 +-
clang/test/CodeGenCXX/constructor-attr.cpp | 15 +++++++
clang/test/CodeGenCXX/destructor-attr.cpp | 23 +++++++++++
...-attribute.c => constructor-attribute.cpp} | 4 +-
7 files changed, 92 insertions(+), 18 deletions(-)
create mode 100644 clang/test/CodeGenCXX/destructor-attr.cpp
rename clang/test/Sema/{constructor-attribute.c => constructor-attribute.cpp} (77%)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 224cb6a32af28..c67aaeda9be70 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1463,9 +1463,13 @@ def ConstInit : InheritableAttr {
def Constructor : InheritableAttr {
let Spellings = [GCC<"constructor">];
- let Args = [DefaultIntArgument<"Priority", 65535>];
+ let Args = [ExprArgument<"Priority", 1>];
let Subjects = SubjectList<[Function]>;
+ let TemplateDependent = 1;
let Documentation = [CtorDtorDocs];
+ let AdditionalMembers = [{
+ static constexpr unsigned int DefaultPriority = 65535;
+ }];
}
def CPUSpecific : InheritableAttr {
@@ -1797,9 +1801,13 @@ def Deprecated : InheritableAttr {
def Destructor : InheritableAttr {
let Spellings = [GCC<"destructor">];
- let Args = [DefaultIntArgument<"Priority", 65535>];
+ let Args = [ExprArgument<"Priority", 1>];
let Subjects = SubjectList<[Function]>;
+ let TemplateDependent = 1;
let Documentation = [CtorDtorDocs];
+ let AdditionalMembers = [{
+ static constexpr unsigned int DefaultPriority = 65535;
+ }];
}
def EmptyBases : InheritableAttr, TargetSpecificAttr<TargetMicrosoftRecordLayout> {
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 834b1c067d84c..1bd6998ca3c75 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6324,10 +6324,20 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
SetLLVMFunctionAttributesForDefinition(D, Fn);
+ auto getPriority = [this](auto *Attr) -> int {
+ int priority = Attr->DefaultPriority;
+ Expr *E = Attr->getPriority();
+ if (E) {
+ if (auto CE = E->getIntegerConstantExpr(this->getContext()))
+ priority = CE->getExtValue();
+ }
+ return priority;
+ };
+
if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>())
- AddGlobalCtor(Fn, CA->getPriority());
+ AddGlobalCtor(Fn, getPriority(CA));
if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())
- AddGlobalDtor(Fn, DA->getPriority(), true);
+ AddGlobalDtor(Fn, getPriority(DA), true);
if (getLangOpts().OpenMP && D->hasAttr<OMPDeclareTargetDeclAttr>())
getOpenMPRuntime().emitDeclareTargetFunction(D, GV);
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 16b18bcb6a2a0..e03ba4c49e328 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2152,29 +2152,44 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL));
}
+static std::optional<Expr *> sharedGetConstructorDestructorAttrExpr(Sema &S, const ParsedAttr &AL) {
+ Expr *E = nullptr;
+ if (AL.getNumArgs() == 1) {
+ E = AL.getArgAsExpr(0);
+ if (E->isValueDependent()) {
+ if (!E->isTypeDependent() && !E->getType()->isIntegerType()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
+ << AL << AANT_ArgumentIntegerConstant << E->getSourceRange();
+ return std::nullopt;
+ }
+ } else {
+ uint32_t priority;
+ if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority)) {
+ return std::nullopt;
+ }
+ }
+ }
+ return E;
+}
+
static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
- uint32_t priority = ConstructorAttr::DefaultPriority;
if (S.getLangOpts().HLSL && AL.getNumArgs()) {
S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
return;
}
- if (AL.getNumArgs() &&
- !S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority))
+ auto E = sharedGetConstructorDestructorAttrExpr(S, AL);
+ if (!E.has_value())
return;
- S.Diag(D->getLocation(), diag::warn_global_constructor)
- << D->getSourceRange();
-
- D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority));
+ S.Diag(D->getLocation(), diag::warn_global_constructor) << D->getSourceRange();
+ D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, E.value()));
}
static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
- uint32_t priority = DestructorAttr::DefaultPriority;
- if (AL.getNumArgs() &&
- !S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority))
+ auto E = sharedGetConstructorDestructorAttrExpr(S, AL);
+ if (!E.has_value())
return;
S.Diag(D->getLocation(), diag::warn_global_destructor) << D->getSourceRange();
-
- D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority));
+ D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, E.value()));
}
template <typename AttrTy>
diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp
index f5a7481571421..a824724abc773 100644
--- a/clang/test/AST/ast-dump-attr.cpp
+++ b/clang/test/AST/ast-dump-attr.cpp
@@ -88,7 +88,8 @@ __attribute__((pointer_with_type_tag(unsigned1,1,2)));
void TestInt(void) __attribute__((constructor(123)));
// CHECK: FunctionDecl{{.*}}TestInt
-// CHECK-NEXT: ConstructorAttr{{.*}} 123
+// CHECK-NEXT: ConstructorAttr
+// CHECK-NEXT: IntegerLiteral{{.*}} 123
static int TestString __attribute__((alias("alias1")));
// CHECK: VarDecl{{.*}}TestString
diff --git a/clang/test/CodeGenCXX/constructor-attr.cpp b/clang/test/CodeGenCXX/constructor-attr.cpp
index ec27ed210ce76..5959d824f78c8 100644
--- a/clang/test/CodeGenCXX/constructor-attr.cpp
+++ b/clang/test/CodeGenCXX/constructor-attr.cpp
@@ -1,6 +1,10 @@
// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
// CHECK: @llvm.global_ctors
+// CHECK-SAME: i32 65535, ptr @_ZN3Foo3fooEv
+// CHECK-SAME: i32 101, ptr @_Z22template_dependent_cxxILi101EEvv
+// CHECK-SAME: i32 102, ptr @_Z22template_dependent_gnuILi102EEvv
+// CHECK-SAME: i32 104, ptr @_Z23template_dependent_nttpIiLi104EEvv
// PR6521
void bar();
@@ -10,3 +14,14 @@ struct Foo {
bar();
}
};
+
+template <int P>
+[[gnu::constructor(P)]] void template_dependent_cxx() {}
+template <int P>
+__attribute__((constructor(P))) void template_dependent_gnu() {}
+template <typename T, int P = sizeof(T) * 26>
+[[gnu::constructor(P)]] void template_dependent_nttp() {}
+
+template void template_dependent_cxx<101>();
+template void template_dependent_gnu<102>();
+template void template_dependent_nttp<int>();
diff --git a/clang/test/CodeGenCXX/destructor-attr.cpp b/clang/test/CodeGenCXX/destructor-attr.cpp
new file mode 100644
index 0000000000000..f0500caed3ccd
--- /dev/null
+++ b/clang/test/CodeGenCXX/destructor-attr.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @llvm.global_dtors
+// CHECK-SAME: i32 65535, ptr @_ZN3Foo3fooEv
+// CHECK-SAME: i32 101, ptr @_Z22template_dependent_cxxILi101EEvv
+// CHECK-SAME: i32 104, ptr @_Z23template_dependent_nttpIiLi104EEvv
+
+// PR6521
+void bar();
+struct Foo {
+ // CHECK-LABEL: define linkonce_odr {{.*}}void @_ZN3Foo3fooEv
+ static void foo() __attribute__((destructor)) {
+ bar();
+ }
+};
+
+template <int P>
+[[gnu::destructor(P)]] void template_dependent_cxx() {}
+template <typename T, int P = sizeof(T) * 26>
+[[gnu::destructor(P)]] void template_dependent_nttp() {}
+
+template void template_dependent_cxx<101>();
+template void template_dependent_nttp<int>();
diff --git a/clang/test/Sema/constructor-attribute.c b/clang/test/Sema/constructor-attribute.cpp
similarity index 77%
rename from clang/test/Sema/constructor-attribute.c
rename to clang/test/Sema/constructor-attribute.cpp
index 2317c7735bda5..6ebb3d6be8a3f 100644
--- a/clang/test/Sema/constructor-attribute.c
+++ b/clang/test/Sema/constructor-attribute.cpp
@@ -6,11 +6,13 @@ 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}}
+template <int *I> [[gnu::constructor(I)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
-int x __attribute__((destructor)); // expected-warning {{'destructor' attribute only applies to functions}}
+int y __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}}
+template <int *I> [[gnu::destructor(I)]] void f(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
void knr() __attribute__((constructor));
>From d3a1e1f38b8f84a437da5e0b22eb5e776bef0f94 Mon Sep 17 00:00:00 2001
From: tynasello <tynasello at gmail.com>
Date: Thu, 14 Aug 2025 18:08:35 +0000
Subject: [PATCH 2/2] [Clang] Add template argument support for
{con,de}structor attributes.
Address PR comments.
---
clang/include/clang/Basic/Attr.td | 2 +-
clang/lib/CodeGen/CodeGenModule.cpp | 12 ++---
clang/lib/Sema/SemaDeclAttr.cpp | 15 ++++--
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 39 +++++++++++++++
clang/test/CodeGenCXX/constructor-attr.cpp | 3 ++
clang/test/Sema/constructor-attribute.cpp | 50 +++++++++++++++++--
6 files changed, 107 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c67aaeda9be70..308fd3d251116 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1468,7 +1468,7 @@ def Constructor : InheritableAttr {
let TemplateDependent = 1;
let Documentation = [CtorDtorDocs];
let AdditionalMembers = [{
- static constexpr unsigned int DefaultPriority = 65535;
+ static constexpr unsigned DefaultPriority = 65535;
}];
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 1bd6998ca3c75..fbf70217ffdd0 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6324,20 +6324,18 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
SetLLVMFunctionAttributesForDefinition(D, Fn);
- auto getPriority = [this](auto *Attr) -> int {
- int priority = Attr->DefaultPriority;
+ auto GetPriority = [this](const auto *Attr) -> int {
Expr *E = Attr->getPriority();
if (E) {
- if (auto CE = E->getIntegerConstantExpr(this->getContext()))
- priority = CE->getExtValue();
+ return E->EvaluateKnownConstInt(this->getContext()).getExtValue();
}
- return priority;
+ return Attr->DefaultPriority;
};
if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>())
- AddGlobalCtor(Fn, getPriority(CA));
+ AddGlobalCtor(Fn, GetPriority(CA));
if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())
- AddGlobalDtor(Fn, getPriority(DA), true);
+ AddGlobalDtor(Fn, GetPriority(DA), true);
if (getLangOpts().OpenMP && D->hasAttr<OMPDeclareTargetDeclAttr>())
getOpenMPRuntime().emitDeclareTargetFunction(D, GV);
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e03ba4c49e328..2d2ca85f2f9a2 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/APValue.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTMutationListener.h"
@@ -58,6 +59,7 @@
#include "clang/Sema/SemaSwift.h"
#include "clang/Sema/SemaWasm.h"
#include "clang/Sema/SemaX86.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Demangle/Demangle.h"
@@ -2152,7 +2154,11 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL));
}
-static std::optional<Expr *> sharedGetConstructorDestructorAttrExpr(Sema &S, const ParsedAttr &AL) {
+static std::optional<Expr *>
+sharedGetConstructorDestructorAttrExpr(Sema &S, const ParsedAttr &AL) {
+ // If no Expr node exists on the attribute, return a nullptr (default priority
+ // to be used). If Expr node exists but is not valid, return a nullopt.
+ // Otherwise, return the Expr.
Expr *E = nullptr;
if (AL.getNumArgs() == 1) {
E = AL.getArgAsExpr(0);
@@ -2167,8 +2173,10 @@ static std::optional<Expr *> sharedGetConstructorDestructorAttrExpr(Sema &S, con
if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority)) {
return std::nullopt;
}
+ return ConstantExpr::Create(S.Context, E,
+ APValue(llvm::APSInt::getUnsigned(priority)));
}
- }
+ }
return E;
}
@@ -2180,7 +2188,8 @@ static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
auto E = sharedGetConstructorDestructorAttrExpr(S, AL);
if (!E.has_value())
return;
- S.Diag(D->getLocation(), diag::warn_global_constructor) << D->getSourceRange();
+ S.Diag(D->getLocation(), diag::warn_global_constructor)
+ << D->getSourceRange();
D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, E.value()));
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 233bb659d37fd..71ada6d6891f1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -233,6 +233,33 @@ static void instantiateDependentAnnotationAttr(
}
}
+template <typename Attr>
+static void sharedInstantiateConstructorDestructorAttr(
+ Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const Attr *A,
+ Decl *New, ASTContext &C) {
+ Expr *tempInstPriority = nullptr;
+ {
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ ExprResult Result = S.SubstExpr(A->getPriority(), TemplateArgs);
+ if (Result.isInvalid())
+ return;
+ tempInstPriority = Result.get();
+ if (auto CE = tempInstPriority->getIntegerConstantExpr(C)) {
+ // Consistent with non-templated priority arguments, which must fit in a
+ // 32-bit unsigned integer.
+ if (!CE->isIntN(32)) {
+ S.Diag(tempInstPriority->getExprLoc(), diag::err_ice_too_large)
+ << toString(*CE, 10, false) << 32 << 1;
+ return;
+ }
+ }
+ }
+ Attr *NewAttr = new (C) Attr(C, *A, tempInstPriority);
+ if (NewAttr)
+ New->addAttr(NewAttr);
+}
+
static Expr *instantiateDependentFunctionAttrCondition(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
@@ -824,6 +851,18 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
continue;
}
+ if (auto *Constructor = dyn_cast<ConstructorAttr>(TmplAttr)) {
+ sharedInstantiateConstructorDestructorAttr(*this, TemplateArgs,
+ Constructor, New, Context);
+ continue;
+ }
+
+ if (auto *Destructor = dyn_cast<DestructorAttr>(TmplAttr)) {
+ sharedInstantiateConstructorDestructorAttr(*this, TemplateArgs,
+ Destructor, New, Context);
+ continue;
+ }
+
if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) {
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
cast<FunctionDecl>(New));
diff --git a/clang/test/CodeGenCXX/constructor-attr.cpp b/clang/test/CodeGenCXX/constructor-attr.cpp
index 5959d824f78c8..d73b661f1ec40 100644
--- a/clang/test/CodeGenCXX/constructor-attr.cpp
+++ b/clang/test/CodeGenCXX/constructor-attr.cpp
@@ -4,6 +4,7 @@
// CHECK-SAME: i32 65535, ptr @_ZN3Foo3fooEv
// CHECK-SAME: i32 101, ptr @_Z22template_dependent_cxxILi101EEvv
// CHECK-SAME: i32 102, ptr @_Z22template_dependent_gnuILi102EEvv
+// CHECK-SAME: i32 103, ptr @_Z1fv
// CHECK-SAME: i32 104, ptr @_Z23template_dependent_nttpIiLi104EEvv
// PR6521
@@ -15,6 +16,7 @@ struct Foo {
}
};
+
template <int P>
[[gnu::constructor(P)]] void template_dependent_cxx() {}
template <int P>
@@ -24,4 +26,5 @@ template <typename T, int P = sizeof(T) * 26>
template void template_dependent_cxx<101>();
template void template_dependent_gnu<102>();
+[[gnu::constructor(103)]] void f() {}
template void template_dependent_nttp<int>();
diff --git a/clang/test/Sema/constructor-attribute.cpp b/clang/test/Sema/constructor-attribute.cpp
index 6ebb3d6be8a3f..159d495f64691 100644
--- a/clang/test/Sema/constructor-attribute.cpp
+++ b/clang/test/Sema/constructor-attribute.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-strict-prototypes %s
+// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wno-strict-prototypes %s
int x __attribute__((constructor)); // expected-warning {{'constructor' attribute only applies to functions}}
int f(void) __attribute__((constructor));
@@ -6,13 +7,56 @@ 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}}
-template <int *I> [[gnu::constructor(I)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
+void knr() __attribute__((constructor));
+
+#ifdef __cplusplus
+template <int *P>
+[[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
+
+template <long long P>
+[[gnu::constructor(P)]] void f() {} // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
+template void f<1LL<<32>(); // expected-note {{in instantiation of function template specialization 'f<4294967296LL>' requested here}}
+template void f<101>();
+
+template <typename T>
+[[gnu::constructor(static_cast<T>(101))]] void f() {}
+template void f<int>();
+template void f<long long>();
+
+template <typename T>
+[[gnu::constructor(static_cast<T>(1LL<<32))]] void g() {}
+template void g<int>();
+
+template <typename T>
+[[gnu::constructor(static_cast<T>(1LL<<32))]] void h() {} // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
+template void h<long long>(); // expected-note {{in instantiation of function template specialization 'h<long long>' requested here}}
+#endif
int y __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}}
-template <int *I> [[gnu::destructor(I)]] void f(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
-void knr() __attribute__((constructor));
+#ifdef __cplusplus
+template <int *I>
+[[gnu::destructor(I)]] void f(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
+
+template <long long P>
+[[gnu::destructor(P)]] void fd() {} // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
+template void fd<1LL<<32>(); // expected-note {{in instantiation of function template specialization 'fd<4294967296LL>' requested here}}
+template void fd<101>();
+
+template <typename T>
+[[gnu::destructor(static_cast<T>(101))]] void fd() {}
+template void fd<int>();
+template void fd<long long>();
+
+template <typename T>
+[[gnu::destructor(static_cast<T>(1LL<<32))]] void gd() {}
+template void gd<int>();
+
+template <typename T>
+[[gnu::destructor(static_cast<T>(1LL<<32))]] void hd() {} // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
+template void hd<long long>(); // expected-note {{in instantiation of function template specialization 'hd<long long>' requested here}}
+#endif
More information about the cfe-commits
mailing list