Skip to content

Commit a205695

Browse files
authored
[Clang] Add template argument support for {con,de}structor attributes. (#151400)
Fixes: #67154 {Con, De}structor attributes in Clang only work with integer priorities (inconsistent with GCC). This commit adds support to these attributes for template arguments. Built off of contributions from [abrachet](https://github.com/abrachet) in [#67376](#67376).
1 parent 0fb2f52 commit a205695

File tree

9 files changed

+211
-31
lines changed

9 files changed

+211
-31
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,9 +1454,13 @@ def ConstInit : InheritableAttr {
14541454

14551455
def Constructor : InheritableAttr {
14561456
let Spellings = [GCC<"constructor">];
1457-
let Args = [DefaultIntArgument<"Priority", 65535>];
1457+
let Args = [ExprArgument<"Priority", 1>];
14581458
let Subjects = SubjectList<[Function]>;
1459+
let TemplateDependent = 1;
14591460
let Documentation = [CtorDtorDocs];
1461+
let AdditionalMembers = [{
1462+
static constexpr unsigned DefaultPriority = 65535;
1463+
}];
14601464
}
14611465

14621466
def CPUSpecific : InheritableAttr {
@@ -1795,9 +1799,13 @@ def Deprecated : InheritableAttr {
17951799

17961800
def Destructor : InheritableAttr {
17971801
let Spellings = [GCC<"destructor">];
1798-
let Args = [DefaultIntArgument<"Priority", 65535>];
1802+
let Args = [ExprArgument<"Priority", 1>];
17991803
let Subjects = SubjectList<[Function]>;
1804+
let TemplateDependent = 1;
18001805
let Documentation = [CtorDtorDocs];
1806+
let AdditionalMembers = [{
1807+
static constexpr unsigned int DefaultPriority = 65535;
1808+
}];
18011809
}
18021810

18031811
def EmptyBases : InheritableAttr, TargetSpecificAttr<TargetMicrosoftRecordLayout> {

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6376,10 +6376,18 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
63766376

63776377
SetLLVMFunctionAttributesForDefinition(D, Fn);
63786378

6379+
auto GetPriority = [this](const auto *Attr) -> int {
6380+
Expr *E = Attr->getPriority();
6381+
if (E) {
6382+
return E->EvaluateKnownConstInt(this->getContext()).getExtValue();
6383+
}
6384+
return Attr->DefaultPriority;
6385+
};
6386+
63796387
if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>())
6380-
AddGlobalCtor(Fn, CA->getPriority());
6388+
AddGlobalCtor(Fn, GetPriority(CA));
63816389
if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())
6382-
AddGlobalDtor(Fn, DA->getPriority(), true);
6390+
AddGlobalDtor(Fn, GetPriority(DA), true);
63836391
if (getLangOpts().OpenMP && D->hasAttr<OMPDeclareTargetDeclAttr>())
63846392
getOpenMPRuntime().emitDeclareTargetFunction(D, GV);
63856393
}

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "clang/AST/APValue.h"
1314
#include "clang/AST/ASTConsumer.h"
1415
#include "clang/AST/ASTContext.h"
1516
#include "clang/AST/ASTMutationListener.h"
@@ -58,6 +59,7 @@
5859
#include "clang/Sema/SemaSwift.h"
5960
#include "clang/Sema/SemaWasm.h"
6061
#include "clang/Sema/SemaX86.h"
62+
#include "llvm/ADT/APSInt.h"
6163
#include "llvm/ADT/STLExtras.h"
6264
#include "llvm/ADT/StringExtras.h"
6365
#include "llvm/Demangle/Demangle.h"
@@ -2156,29 +2158,51 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
21562158
D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL));
21572159
}
21582160

2161+
static ExprResult sharedGetConstructorDestructorAttrExpr(Sema &S,
2162+
const ParsedAttr &AL) {
2163+
// If no Expr node exists on the attribute, return a nullptr result (default
2164+
// priority to be used). If Expr node exists but is not valid, return an
2165+
// invalid result. Otherwise, return the Expr.
2166+
Expr *E = nullptr;
2167+
if (AL.getNumArgs() == 1) {
2168+
E = AL.getArgAsExpr(0);
2169+
if (E->isValueDependent()) {
2170+
if (!E->isTypeDependent() && !E->getType()->isIntegerType()) {
2171+
S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
2172+
<< AL << AANT_ArgumentIntegerConstant << E->getSourceRange();
2173+
return ExprError();
2174+
}
2175+
} else {
2176+
uint32_t priority;
2177+
if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority)) {
2178+
return ExprError();
2179+
}
2180+
return ConstantExpr::Create(S.Context, E,
2181+
APValue(llvm::APSInt::getUnsigned(priority)));
2182+
}
2183+
}
2184+
return E;
2185+
}
2186+
21592187
static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
2160-
uint32_t priority = ConstructorAttr::DefaultPriority;
21612188
if (S.getLangOpts().HLSL && AL.getNumArgs()) {
21622189
S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
21632190
return;
21642191
}
2165-
if (AL.getNumArgs() &&
2166-
!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority))
2192+
ExprResult E = sharedGetConstructorDestructorAttrExpr(S, AL);
2193+
if (E.isInvalid())
21672194
return;
21682195
S.Diag(D->getLocation(), diag::warn_global_constructor)
21692196
<< D->getSourceRange();
2170-
2171-
D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority));
2197+
D->addAttr(ConstructorAttr::Create(S.Context, E.get(), AL));
21722198
}
21732199

21742200
static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
2175-
uint32_t priority = DestructorAttr::DefaultPriority;
2176-
if (AL.getNumArgs() &&
2177-
!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority))
2201+
ExprResult E = sharedGetConstructorDestructorAttrExpr(S, AL);
2202+
if (E.isInvalid())
21782203
return;
21792204
S.Diag(D->getLocation(), diag::warn_global_destructor) << D->getSourceRange();
2180-
2181-
D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority));
2205+
D->addAttr(DestructorAttr::Create(S.Context, E.get(), AL));
21822206
}
21832207

21842208
template <typename AttrTy>

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,32 @@ static void instantiateDependentAnnotationAttr(
234234
}
235235
}
236236

237+
template <typename Attr>
238+
static void sharedInstantiateConstructorDestructorAttr(
239+
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const Attr *A,
240+
Decl *New, ASTContext &C) {
241+
Expr *tempInstPriority = nullptr;
242+
{
243+
EnterExpressionEvaluationContext Unevaluated(
244+
S, Sema::ExpressionEvaluationContext::Unevaluated);
245+
ExprResult Result = S.SubstExpr(A->getPriority(), TemplateArgs);
246+
if (Result.isInvalid())
247+
return;
248+
tempInstPriority = Result.get();
249+
if (std::optional<llvm::APSInt> CE =
250+
tempInstPriority->getIntegerConstantExpr(C)) {
251+
// Consistent with non-templated priority arguments, which must fit in a
252+
// 32-bit unsigned integer.
253+
if (!CE->isIntN(32)) {
254+
S.Diag(tempInstPriority->getExprLoc(), diag::err_ice_too_large)
255+
<< toString(*CE, 10, false) << /*Size=*/32 << /*Unsigned=*/1;
256+
return;
257+
}
258+
}
259+
}
260+
New->addAttr(Attr::Create(C, tempInstPriority, *A));
261+
}
262+
237263
static Expr *instantiateDependentFunctionAttrCondition(
238264
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
239265
const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
@@ -825,6 +851,18 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
825851
continue;
826852
}
827853

854+
if (auto *Constructor = dyn_cast<ConstructorAttr>(TmplAttr)) {
855+
sharedInstantiateConstructorDestructorAttr(*this, TemplateArgs,
856+
Constructor, New, Context);
857+
continue;
858+
}
859+
860+
if (auto *Destructor = dyn_cast<DestructorAttr>(TmplAttr)) {
861+
sharedInstantiateConstructorDestructorAttr(*this, TemplateArgs,
862+
Destructor, New, Context);
863+
continue;
864+
}
865+
828866
if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) {
829867
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
830868
cast<FunctionDecl>(New));

clang/test/AST/ast-dump-attr.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ __attribute__((pointer_with_type_tag(unsigned1,1,2)));
8888

8989
void TestInt(void) __attribute__((constructor(123)));
9090
// CHECK: FunctionDecl{{.*}}TestInt
91-
// CHECK-NEXT: ConstructorAttr{{.*}} 123
91+
// CHECK-NEXT: ConstructorAttr
92+
// CHECK-NEXT: ConstantExpr
93+
// CHECK-NEXT: value: Int 123
94+
// CHECK-NEXT: IntegerLiteral{{.*}} 123
9295

9396
static int TestString __attribute__((alias("alias1")));
9497
// CHECK: VarDecl{{.*}}TestString

clang/test/CodeGenCXX/constructor-attr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
22

33
// CHECK: @llvm.global_ctors
4+
// CHECK-SAME: i32 65535, ptr @_ZN3Foo3fooEv
5+
// CHECK-SAME: i32 101, ptr @_Z22template_dependent_cxxILi101EEvv
6+
// CHECK-SAME: i32 102, ptr @_Z22template_dependent_gnuILi102EEvv
7+
// CHECK-SAME: i32 103, ptr @_Z1fv
8+
// CHECK-SAME: i32 104, ptr @_Z23template_dependent_nttpIiLi104EEvv
49

510
// PR6521
611
void bar();
@@ -10,3 +15,16 @@ struct Foo {
1015
bar();
1116
}
1217
};
18+
19+
20+
template <int P>
21+
[[gnu::constructor(P)]] void template_dependent_cxx() {}
22+
template <int P>
23+
__attribute__((constructor(P))) void template_dependent_gnu() {}
24+
template <typename T, int P = sizeof(T) * 26>
25+
[[gnu::constructor(P)]] void template_dependent_nttp() {}
26+
27+
template void template_dependent_cxx<101>();
28+
template void template_dependent_gnu<102>();
29+
[[gnu::constructor(103)]] void f() {}
30+
template void template_dependent_nttp<int>();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
2+
3+
// CHECK: @llvm.global_dtors
4+
// CHECK-SAME: i32 65535, ptr @_ZN3Foo3fooEv
5+
// CHECK-SAME: i32 101, ptr @_Z22template_dependent_cxxILi101EEvv
6+
// CHECK-SAME: i32 104, ptr @_Z23template_dependent_nttpIiLi104EEvv
7+
8+
// PR6521
9+
void bar();
10+
struct Foo {
11+
// CHECK-LABEL: define linkonce_odr {{.*}}void @_ZN3Foo3fooEv
12+
static void foo() __attribute__((destructor)) {
13+
bar();
14+
}
15+
};
16+
17+
template <int P>
18+
[[gnu::destructor(P)]] void template_dependent_cxx() {}
19+
template <typename T, int P = sizeof(T) * 26>
20+
[[gnu::destructor(P)]] void template_dependent_nttp() {}
21+
22+
template void template_dependent_cxx<101>();
23+
template void template_dependent_nttp<int>();

clang/test/Sema/constructor-attribute.c

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify -Wno-strict-prototypes %s
2+
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wno-strict-prototypes %s
3+
4+
int x __attribute__((constructor)); // expected-warning {{'constructor' attribute only applies to functions}}
5+
int f(void) __attribute__((constructor));
6+
int f(void) __attribute__((constructor(1)));
7+
int f(void) __attribute__((constructor(1,2))); // expected-error {{'constructor' attribute takes no more than 1 argument}}
8+
int f(void) __attribute__((constructor(1.0))); // expected-error {{'constructor' attribute requires an integer constant}}
9+
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}}
10+
void knr() __attribute__((constructor));
11+
12+
#ifdef __cplusplus
13+
template <float P> [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
14+
template <double P> [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
15+
template <int *P> [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
16+
17+
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}}
18+
template void f<1LL<<32>(); // expected-note {{in instantiation of function template specialization 'f<4294967296LL>' requested here}}
19+
template void f<101>();
20+
21+
template <typename T> [[gnu::constructor(static_cast<T>(1LL<<32))]] void f() {} // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
22+
template void f<long long>(); // expected-note {{in instantiation of function template specialization 'f<long long>' requested here}}
23+
template void f<int>();
24+
25+
template <typename T>
26+
[[gnu::constructor(static_cast<T>(101))]] void g() {}
27+
template void g<int>();
28+
template void g<long long>();
29+
30+
template <typename T>
31+
[[gnu::constructor(static_cast<T>(T{101}))]] void h() {}
32+
template void h<int>();
33+
template void h<long long>();
34+
35+
template <typename T>
36+
[[gnu::constructor(static_cast<T>(sizeof(T[101])))]] void a() {}
37+
template void a<int>();
38+
template void a<long long>();
39+
#endif
40+
41+
int yd __attribute__((destructor)); // expected-warning {{'destructor' attribute only applies to functions}}
42+
int fd(void) __attribute__((destructor));
43+
int fd(void) __attribute__((destructor(1)));
44+
int fd(void) __attribute__((destructor(1,2))); // expected-error {{'destructor' attribute takes no more than 1 argument}}
45+
int fd(void) __attribute__((destructor(1.0))); // expected-error {{'destructor' attribute requires an integer constant}}
46+
47+
#ifdef __cplusplus
48+
template <float P> [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
49+
template <double P> [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
50+
template <int *P> [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
51+
52+
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}}
53+
template void fd<1LL<<32>(); // expected-note {{in instantiation of function template specialization 'fd<4294967296LL>' requested here}}
54+
template void fd<101>();
55+
56+
template <typename T> [[gnu::destructor(static_cast<T>(1LL<<32))]] void fd() {} // expected-error {{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}}
57+
template void fd<long long>(); // expected-note {{in instantiation of function template specialization 'fd<long long>' requested here}}
58+
template void fd<int>();
59+
60+
template <typename T>
61+
[[gnu::destructor(static_cast<T>(101))]] void gd() {}
62+
template void gd<int>();
63+
template void gd<long long>();
64+
65+
template <typename T>
66+
[[gnu::destructor(static_cast<T>(T{101}))]] void hd() {}
67+
template void hd<int>();
68+
template void hd<long long>();
69+
70+
template <typename T>
71+
[[gnu::destructor(static_cast<T>(sizeof(T[101])))]] void ad() {}
72+
template void ad<int>();
73+
template void ad<long long>();
74+
#endif

0 commit comments

Comments
 (0)