[Clang] Add template argument support for {con,de}structor attributes. (#151400)
Fixes: https://github.com/llvm/llvm-project/issues/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](https://github.com/llvm/llvm-project/pull/67376).
This commit is contained in:
@@ -1454,9 +1454,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 DefaultPriority = 65535;
|
||||
}];
|
||||
}
|
||||
|
||||
def CPUSpecific : InheritableAttr {
|
||||
@@ -1795,9 +1799,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> {
|
||||
|
||||
@@ -6376,10 +6376,18 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
|
||||
|
||||
SetLLVMFunctionAttributesForDefinition(D, Fn);
|
||||
|
||||
auto GetPriority = [this](const auto *Attr) -> int {
|
||||
Expr *E = Attr->getPriority();
|
||||
if (E) {
|
||||
return E->EvaluateKnownConstInt(this->getContext()).getExtValue();
|
||||
}
|
||||
return Attr->DefaultPriority;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -2156,29 +2158,51 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL));
|
||||
}
|
||||
|
||||
static ExprResult sharedGetConstructorDestructorAttrExpr(Sema &S,
|
||||
const ParsedAttr &AL) {
|
||||
// If no Expr node exists on the attribute, return a nullptr result (default
|
||||
// priority to be used). If Expr node exists but is not valid, return an
|
||||
// invalid result. Otherwise, return the Expr.
|
||||
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 ExprError();
|
||||
}
|
||||
} else {
|
||||
uint32_t priority;
|
||||
if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority)) {
|
||||
return ExprError();
|
||||
}
|
||||
return ConstantExpr::Create(S.Context, E,
|
||||
APValue(llvm::APSInt::getUnsigned(priority)));
|
||||
}
|
||||
}
|
||||
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))
|
||||
ExprResult E = sharedGetConstructorDestructorAttrExpr(S, AL);
|
||||
if (E.isInvalid())
|
||||
return;
|
||||
S.Diag(D->getLocation(), diag::warn_global_constructor)
|
||||
<< D->getSourceRange();
|
||||
|
||||
D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority));
|
||||
D->addAttr(ConstructorAttr::Create(S.Context, E.get(), AL));
|
||||
}
|
||||
|
||||
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))
|
||||
ExprResult E = sharedGetConstructorDestructorAttrExpr(S, AL);
|
||||
if (E.isInvalid())
|
||||
return;
|
||||
S.Diag(D->getLocation(), diag::warn_global_destructor) << D->getSourceRange();
|
||||
|
||||
D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority));
|
||||
D->addAttr(DestructorAttr::Create(S.Context, E.get(), AL));
|
||||
}
|
||||
|
||||
template <typename AttrTy>
|
||||
|
||||
@@ -234,6 +234,32 @@ 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 (std::optional<llvm::APSInt> 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) << /*Size=*/32 << /*Unsigned=*/1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
New->addAttr(Attr::Create(C, tempInstPriority, *A));
|
||||
}
|
||||
|
||||
static Expr *instantiateDependentFunctionAttrCondition(
|
||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
|
||||
@@ -825,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));
|
||||
|
||||
@@ -88,7 +88,10 @@ __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: ConstantExpr
|
||||
// CHECK-NEXT: value: Int 123
|
||||
// CHECK-NEXT: IntegerLiteral{{.*}} 123
|
||||
|
||||
static int TestString __attribute__((alias("alias1")));
|
||||
// CHECK: VarDecl{{.*}}TestString
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// 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 103, ptr @_Z1fv
|
||||
// CHECK-SAME: i32 104, ptr @_Z23template_dependent_nttpIiLi104EEvv
|
||||
|
||||
// PR6521
|
||||
void bar();
|
||||
@@ -10,3 +15,16 @@ 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>();
|
||||
[[gnu::constructor(103)]] void f() {}
|
||||
template void template_dependent_nttp<int>();
|
||||
|
||||
23
clang/test/CodeGenCXX/destructor-attr.cpp
Normal file
23
clang/test/CodeGenCXX/destructor-attr.cpp
Normal file
@@ -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>();
|
||||
@@ -1,16 +0,0 @@
|
||||
// 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));
|
||||
74
clang/test/Sema/constructor-attribute.cpp
Normal file
74
clang/test/Sema/constructor-attribute.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -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));
|
||||
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}}
|
||||
void knr() __attribute__((constructor));
|
||||
|
||||
#ifdef __cplusplus
|
||||
template <float P> [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
|
||||
template <double P> [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}}
|
||||
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>(1LL<<32))]] 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<long long>(); // expected-note {{in instantiation of function template specialization 'f<long long>' requested here}}
|
||||
template void f<int>();
|
||||
|
||||
template <typename T>
|
||||
[[gnu::constructor(static_cast<T>(101))]] void g() {}
|
||||
template void g<int>();
|
||||
template void g<long long>();
|
||||
|
||||
template <typename T>
|
||||
[[gnu::constructor(static_cast<T>(T{101}))]] void h() {}
|
||||
template void h<int>();
|
||||
template void h<long long>();
|
||||
|
||||
template <typename T>
|
||||
[[gnu::constructor(static_cast<T>(sizeof(T[101])))]] void a() {}
|
||||
template void a<int>();
|
||||
template void a<long long>();
|
||||
#endif
|
||||
|
||||
int yd __attribute__((destructor)); // expected-warning {{'destructor' attribute only applies to functions}}
|
||||
int fd(void) __attribute__((destructor));
|
||||
int fd(void) __attribute__((destructor(1)));
|
||||
int fd(void) __attribute__((destructor(1,2))); // expected-error {{'destructor' attribute takes no more than 1 argument}}
|
||||
int fd(void) __attribute__((destructor(1.0))); // expected-error {{'destructor' attribute requires an integer constant}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
template <float P> [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
|
||||
template <double P> [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}}
|
||||
template <int *P> [[gnu::destructor(P)]] void fd(); // 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>(1LL<<32))]] 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<long long>(); // expected-note {{in instantiation of function template specialization 'fd<long long>' requested here}}
|
||||
template void fd<int>();
|
||||
|
||||
template <typename T>
|
||||
[[gnu::destructor(static_cast<T>(101))]] void gd() {}
|
||||
template void gd<int>();
|
||||
template void gd<long long>();
|
||||
|
||||
template <typename T>
|
||||
[[gnu::destructor(static_cast<T>(T{101}))]] void hd() {}
|
||||
template void hd<int>();
|
||||
template void hd<long long>();
|
||||
|
||||
template <typename T>
|
||||
[[gnu::destructor(static_cast<T>(sizeof(T[101])))]] void ad() {}
|
||||
template void ad<int>();
|
||||
template void ad<long long>();
|
||||
#endif
|
||||
Reference in New Issue
Block a user