diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0fb0e7178404e..e672d857b9a97 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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 { diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f1ddaa875f3e4..b9685f82e1534 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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()) - AddGlobalCtor(Fn, CA->getPriority()); + AddGlobalCtor(Fn, GetPriority(CA)); if (const DestructorAttr *DA = D->getAttr()) - AddGlobalDtor(Fn, DA->getPriority(), true); + AddGlobalDtor(Fn, GetPriority(DA), true); if (getLangOpts().OpenMP && D->hasAttr()) getOpenMPRuntime().emitDeclareTargetFunction(D, GV); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 3ded60cd8b073..cb2c132cca978 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" @@ -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 diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ee1b520fa46e9..c9e7fe9f44ff2 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -234,6 +234,32 @@ static void instantiateDependentAnnotationAttr( } } +template +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 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(TmplAttr)) { + sharedInstantiateConstructorDestructorAttr(*this, TemplateArgs, + Constructor, New, Context); + continue; + } + + if (auto *Destructor = dyn_cast(TmplAttr)) { + sharedInstantiateConstructorDestructorAttr(*this, TemplateArgs, + Destructor, New, Context); + continue; + } + if (const auto *EnableIf = dyn_cast(TmplAttr)) { instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl, cast(New)); diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp index f5a7481571421..e66990068cb7a 100644 --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -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 diff --git a/clang/test/CodeGenCXX/constructor-attr.cpp b/clang/test/CodeGenCXX/constructor-attr.cpp index ec27ed210ce76..d73b661f1ec40 100644 --- a/clang/test/CodeGenCXX/constructor-attr.cpp +++ b/clang/test/CodeGenCXX/constructor-attr.cpp @@ -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 +[[gnu::constructor(P)]] void template_dependent_cxx() {} +template +__attribute__((constructor(P))) void template_dependent_gnu() {} +template +[[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(); 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 +[[gnu::destructor(P)]] void template_dependent_cxx() {} +template +[[gnu::destructor(P)]] void template_dependent_nttp() {} + +template void template_dependent_cxx<101>(); +template void template_dependent_nttp(); diff --git a/clang/test/Sema/constructor-attribute.c b/clang/test/Sema/constructor-attribute.c deleted file mode 100644 index 2317c7735bda5..0000000000000 --- a/clang/test/Sema/constructor-attribute.c +++ /dev/null @@ -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)); diff --git a/clang/test/Sema/constructor-attribute.cpp b/clang/test/Sema/constructor-attribute.cpp new file mode 100644 index 0000000000000..81e9cb3141f1e --- /dev/null +++ b/clang/test/Sema/constructor-attribute.cpp @@ -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 [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}} +template [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}} +template [[gnu::constructor(P)]] void f(); // expected-error {{'gnu::constructor' attribute requires an integer constant}} + +template [[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 [[gnu::constructor(static_cast(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(); // expected-note {{in instantiation of function template specialization 'f' requested here}} +template void f(); + +template +[[gnu::constructor(static_cast(101))]] void g() {} +template void g(); +template void g(); + +template +[[gnu::constructor(static_cast(T{101}))]] void h() {} +template void h(); +template void h(); + +template +[[gnu::constructor(static_cast(sizeof(T[101])))]] void a() {} +template void a(); +template void a(); +#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 [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}} +template [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}} +template [[gnu::destructor(P)]] void fd(); // expected-error {{'gnu::destructor' attribute requires an integer constant}} + +template [[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 [[gnu::destructor(static_cast(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(); // expected-note {{in instantiation of function template specialization 'fd' requested here}} +template void fd(); + +template +[[gnu::destructor(static_cast(101))]] void gd() {} +template void gd(); +template void gd(); + +template +[[gnu::destructor(static_cast(T{101}))]] void hd() {} +template void hd(); +template void hd(); + +template +[[gnu::destructor(static_cast(sizeof(T[101])))]] void ad() {} +template void ad(); +template void ad(); +#endif