Skip to content

Commit 27ecb63

Browse files
committed
Diagnose problematic uses of constructor/destructor attribute (#67673)
Functions with these attributes will be automatically called before main() or after main() exits gracefully. In glibc environments, the constructor function is passed the same arguments as main(), so that signature is allowed. In all other environments, we require the function to accept no arguments and either return `void` or `int`. The functions must use the C calling convention. In C++ language modes, the functions cannot be a nonstatic member function, or a consteval function. 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, but we downgrade the error to be a warning which defaults to an error to ease use for implementers like compiler-rt or libc. This relands a633a37 with fixes.
1 parent 670034c commit 27ecb63

File tree

15 files changed

+260
-91
lines changed

15 files changed

+260
-91
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ Attribute Changes in Clang
217217
automatic diagnostic to use parameters of types that the format style
218218
supports but that are never the result of default argument promotion, such as
219219
``float``. (`#59824: <https://github.com/llvm/llvm-project/issues/59824>`_)
220+
- The ``constructor`` and ``destructor`` attributes now diagnose when:
221+
- the priority is not between 101 and 65535, inclusive,
222+
- the function it is applied to accepts arguments or has a non-void return
223+
type, or
224+
- the function it is applied to is a non-static member function (C++).
220225

221226
Improvements to Clang's diagnostics
222227
-----------------------------------

clang/include/clang/Basic/AttrDocs.td

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7255,8 +7255,14 @@ after returning from ``main()`` or when the ``exit()`` function has been
72557255
called. Note, ``quick_exit()``, ``_Exit()``, and ``abort()`` prevent a function
72567256
marked ``destructor`` from being called.
72577257

7258-
The constructor or destructor function should not accept any arguments and its
7259-
return type should be ``void``.
7258+
In general, the constructor or destructor function must use the C calling
7259+
convention, cannot accept any arguments, and its return type should be
7260+
``void``, ``int``, or ``unsigned int``. The latter two types are supported for
7261+
historical reasons. On targets with a GNU environment (one which uses glibc),
7262+
the signature of the function can also be the same as that of ``main()``.
7263+
7264+
In C++ language modes, the function cannot be marked ``consteval``, nor can it
7265+
be a non-static member function.
72607266

72617267
The attributes accept an optional argument used to specify the priority order
72627268
in which to execute constructor and destructor functions. The priority is

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ def EnumConversion : DiagGroup<"enum-conversion",
105105
[EnumEnumConversion,
106106
EnumFloatConversion,
107107
EnumCompareConditional]>;
108+
def InvalidPriority : DiagGroup<"priority-ctor-dtor">;
109+
// For compatibility with GCC.
110+
def : DiagGroup<"prio-ctor-dtor", [InvalidPriority]>;
111+
108112
def ObjCSignedCharBoolImplicitIntConversion :
109113
DiagGroup<"objc-signed-char-bool-implicit-int-conversion">;
110114
def ImplicitIntConversion : DiagGroup<"implicit-int-conversion",

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2870,6 +2870,8 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning<
28702870
InGroup<CXXPre14Compat>, DefaultIgnore;
28712871
def note_constexpr_body_previous_return : Note<
28722872
"previous return statement is here">;
2873+
def err_ctordtor_attr_consteval : Error<
2874+
"%0 attribute cannot be applied to a 'consteval' function">;
28732875

28742876
// C++20 function try blocks in constexpr
28752877
def ext_constexpr_function_try_block_cxx20 : ExtWarn<
@@ -3182,6 +3184,13 @@ def err_alignas_underaligned : Error<
31823184
"requested alignment is less than minimum alignment of %1 for type %0">;
31833185
def warn_aligned_attr_underaligned : Warning<err_alignas_underaligned.Summary>,
31843186
InGroup<IgnoredAttributes>;
3187+
def err_ctor_dtor_attr_on_non_void_func : Error<
3188+
"%0 attribute can only be applied to a function which accepts no arguments "
3189+
"and has a 'void' or 'int' return type">;
3190+
def err_ctor_dtor_member_func : Error<
3191+
"%0 attribute cannot be applied to a member function">;
3192+
def err_ctor_dtor_calling_conv : Error<
3193+
"%0 attribute must be applied to a function with the C calling convention">;
31853194
def err_attribute_sizeless_type : Error<
31863195
"%0 attribute cannot be applied to sizeless type %1">;
31873196
def err_attribute_argument_n_type : Error<
@@ -3195,6 +3204,9 @@ def err_attribute_argument_out_of_range : Error<
31953204
def err_init_priority_object_attr : Error<
31963205
"can only use 'init_priority' attribute on file-scope definitions "
31973206
"of objects of class type">;
3207+
def warn_priority_out_of_range : Warning<
3208+
err_attribute_argument_out_of_range.Summary>,
3209+
InGroup<InvalidPriority>, DefaultError;
31983210
def err_attribute_argument_out_of_bounds : Error<
31993211
"%0 attribute parameter %1 is out of bounds">;
32003212
def err_attribute_only_once_per_parameter : Error<

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,26 +2352,126 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
23522352
D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL));
23532353
}
23542354

2355-
static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
2356-
uint32_t priority = ConstructorAttr::DefaultPriority;
2355+
static void diagnoseInvalidPriority(Sema &S, uint32_t Priority,
2356+
const ParsedAttr &A,
2357+
SourceLocation PriorityLoc) {
2358+
constexpr uint32_t ReservedPriorityLower = 101, ReservedPriorityUpper = 65535;
2359+
2360+
// Only perform the priority check if the attribute is outside of a system
2361+
// header. Values <= 100 are reserved for the implementation, and libc++
2362+
// benefits from being able to specify values in that range. Values > 65535
2363+
// are reserved for historical reasons.
2364+
if ((Priority < ReservedPriorityLower || Priority > ReservedPriorityUpper) &&
2365+
!S.getSourceManager().isInSystemHeader(A.getLoc())) {
2366+
S.Diag(A.getLoc(), diag::warn_priority_out_of_range)
2367+
<< PriorityLoc << A << ReservedPriorityLower << ReservedPriorityUpper;
2368+
}
2369+
}
2370+
2371+
static bool FunctionParamsAreMainLike(ASTContext &Context,
2372+
const FunctionDecl *FD) {
2373+
assert(FD->hasPrototype() && "expected the function to have a prototype");
2374+
const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
2375+
QualType CharPP =
2376+
Context.getPointerType(Context.getPointerType(Context.CharTy));
2377+
QualType Expected[] = {Context.IntTy, CharPP, CharPP, CharPP};
2378+
for (unsigned I = 0;
2379+
I < sizeof(Expected) / sizeof(QualType) && I < FPT->getNumParams();
2380+
++I) {
2381+
QualType AT = FPT->getParamType(I);
2382+
2383+
if (!Context.hasSameUnqualifiedType(AT, Expected[I])) {
2384+
if (Expected[I] == CharPP) {
2385+
// As an extension, the following forms are okay:
2386+
// char const **
2387+
// char const * const *
2388+
// char * const *
2389+
2390+
QualifierCollector Qs;
2391+
const PointerType *PT;
2392+
if ((PT = Qs.strip(AT)->getAs<PointerType>()) &&
2393+
(PT = Qs.strip(PT->getPointeeType())->getAs<PointerType>()) &&
2394+
Context.hasSameType(QualType(Qs.strip(PT->getPointeeType()), 0),
2395+
Context.CharTy)) {
2396+
Qs.removeConst();
2397+
if (!Qs.empty())
2398+
return false;
2399+
continue; // Accepted as an extension.
2400+
}
2401+
}
2402+
return false;
2403+
}
2404+
}
2405+
return true;
2406+
}
2407+
2408+
template <typename CtorDtorAttr>
2409+
static void handleCtorDtorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
2410+
uint32_t Priority = CtorDtorAttr::DefaultPriority;
23572411
if (S.getLangOpts().HLSL && AL.getNumArgs()) {
23582412
S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
23592413
return;
23602414
}
2361-
if (AL.getNumArgs() &&
2362-
!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority))
2363-
return;
23642415

2365-
D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority));
2366-
}
2416+
// If we're given an argument for the priority, check that it's valid.
2417+
if (AL.getNumArgs()) {
2418+
if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), Priority))
2419+
return;
2420+
2421+
// Diagnose an invalid priority, but continue to process the attribute.
2422+
diagnoseInvalidPriority(S, Priority, AL, AL.getArgAsExpr(0)->getExprLoc());
2423+
}
23672424

2368-
static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
2369-
uint32_t priority = DestructorAttr::DefaultPriority;
2370-
if (AL.getNumArgs() &&
2371-
!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority))
2425+
// Ensure the function we're attaching to is something that is sensible to
2426+
// automatically call before or after main(); it should accept no arguments.
2427+
// In theory, a void return type is the only truly safe return type (consider
2428+
// that calling conventions may place returned values in a hidden pointer
2429+
// argument passed to the function that will not be present when called
2430+
// automatically). However, there is a significant amount of existing code
2431+
// which uses an int return type. So we will accept void, int, and
2432+
// unsigned int return types. Any other return type, or a non-void parameter
2433+
// list is treated as an error because it's a form of type system
2434+
// incompatibility. The function also cannot be a member function. We allow
2435+
// K&R C functions because that's a difficult edge case where it depends on
2436+
// how the function is defined as to whether it does or does not expect
2437+
// arguments.
2438+
//
2439+
// However! glibc on ELF will pass the same arguments to a constructor
2440+
// function as are given to main(), so we will allow `int, char *[]` and
2441+
// `int, char *[], char *[]` (or qualified versions thereof), but only if
2442+
// the target is explicitly for glibc.
2443+
const auto *FD = cast<FunctionDecl>(D);
2444+
QualType RetTy = FD->getReturnType();
2445+
bool IsGlibC = S.Context.getTargetInfo().getTriple().isGNUEnvironment();
2446+
if (!(RetTy->isVoidType() ||
2447+
RetTy->isSpecificBuiltinType(BuiltinType::UInt) ||
2448+
RetTy->isSpecificBuiltinType(BuiltinType::Int)) ||
2449+
FD->isVariadic() ||
2450+
(FD->hasPrototype() &&
2451+
((!IsGlibC && FD->getNumParams() != 0) ||
2452+
(IsGlibC && !FunctionParamsAreMainLike(S.Context, FD))))) {
2453+
S.Diag(AL.getLoc(), diag::err_ctor_dtor_attr_on_non_void_func)
2454+
<< AL << FD->getSourceRange();
2455+
return;
2456+
}
2457+
if (FD->getType()->castAs<FunctionType>()->getCallConv() !=
2458+
CallingConv::CC_C) {
2459+
S.Diag(AL.getLoc(), diag::err_ctor_dtor_calling_conv)
2460+
<< AL << FD->getSourceRange();
2461+
return;
2462+
}
2463+
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); MD && MD->isInstance()) {
2464+
S.Diag(AL.getLoc(), diag::err_ctor_dtor_member_func)
2465+
<< AL << FD->getSourceRange();
2466+
return;
2467+
}
2468+
if (FD->isConsteval()) {
2469+
S.Diag(AL.getLoc(), diag::err_ctordtor_attr_consteval)
2470+
<< AL << FD->getSourceRange();
23722471
return;
2472+
}
23732473

2374-
D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority));
2474+
D->addAttr(CtorDtorAttr::Create(S.Context, Priority, AL));
23752475
}
23762476

23772477
template <typename AttrTy>
@@ -3888,16 +3988,9 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
38883988
return;
38893989
}
38903990

3891-
// Only perform the priority check if the attribute is outside of a system
3892-
// header. Values <= 100 are reserved for the implementation, and libc++
3893-
// benefits from being able to specify values in that range.
3894-
if ((prioritynum < 101 || prioritynum > 65535) &&
3895-
!S.getSourceManager().isInSystemHeader(AL.getLoc())) {
3896-
S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_range)
3897-
<< E->getSourceRange() << AL << 101 << 65535;
3898-
AL.setInvalid();
3899-
return;
3900-
}
3991+
// Diagnose an invalid priority, but continue to process the attribute.
3992+
diagnoseInvalidPriority(S, prioritynum, AL, E->getExprLoc());
3993+
39013994
D->addAttr(::new (S.Context) InitPriorityAttr(S.Context, AL, prioritynum));
39023995
}
39033996

@@ -8959,13 +9052,13 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
89599052
handlePassObjectSizeAttr(S, D, AL);
89609053
break;
89619054
case ParsedAttr::AT_Constructor:
8962-
handleConstructorAttr(S, D, AL);
9055+
handleCtorDtorAttr<ConstructorAttr>(S, D, AL);
89639056
break;
89649057
case ParsedAttr::AT_Deprecated:
89659058
handleDeprecatedAttr(S, D, AL);
89669059
break;
89679060
case ParsedAttr::AT_Destructor:
8968-
handleDestructorAttr(S, D, AL);
9061+
handleCtorDtorAttr<DestructorAttr>(S, D, AL);
89699062
break;
89709063
case ParsedAttr::AT_EnableIf:
89719064
handleEnableIfAttr(S, D, AL);

clang/test/CodeGen/PowerPC/aix-destructor-attribute.c

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
// RUN: -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \
1313
// RUN: FileCheck --check-prefix=REGISTER %s
1414

15-
int bar(void) __attribute__((destructor(100)));
15+
int bar(void) __attribute__((destructor(101)));
1616
int bar2(void) __attribute__((destructor(65535)));
17-
int bar3(int) __attribute__((destructor(65535)));
1817

1918
int bar(void) {
2019
return 1;
@@ -24,16 +23,12 @@ int bar2(void) {
2423
return 2;
2524
}
2625

27-
int bar3(int a) {
28-
return a;
29-
}
30-
31-
// 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 }]
26+
// 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 }]
3227

33-
// 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 }]
34-
// 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 }]
28+
// 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 }]
29+
// 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 }]
3530

36-
// REGISTER: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] {
31+
// REGISTER: define internal void @__GLOBAL_init_101() [[ATTR:#[0-9]+]] {
3732
// REGISTER: entry:
3833
// REGISTER: %0 = call i32 @atexit(ptr @bar)
3934
// REGISTER: ret void
@@ -42,11 +37,10 @@ int bar3(int a) {
4237
// REGISTER: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] {
4338
// REGISTER: entry:
4439
// REGISTER: %0 = call i32 @atexit(ptr @bar2)
45-
// REGISTER: %1 = call i32 @atexit(ptr @bar3)
4640
// REGISTER: ret void
4741
// REGISTER: }
4842

49-
// REGISTER: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] {
43+
// REGISTER: define internal void @__GLOBAL_cleanup_101() [[ATTR:#[0-9]+]] {
5044
// REGISTER: entry:
5145
// REGISTER: %0 = call i32 @unatexit(ptr @bar)
5246
// REGISTER: %needs_destruct = icmp eq i32 %0, 0
@@ -62,20 +56,11 @@ int bar3(int a) {
6256

6357
// REGISTER: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] {
6458
// REGISTER: entry:
65-
// REGISTER: %0 = call i32 @unatexit(ptr @bar3)
59+
// REGISTER: %0 = call i32 @unatexit(ptr @bar2)
6660
// REGISTER: %needs_destruct = icmp eq i32 %0, 0
67-
// REGISTER: br i1 %needs_destruct, label %destruct.call, label %unatexit.call
61+
// REGISTER: br i1 %needs_destruct, label %destruct.call, label %destruct.end
6862

6963
// REGISTER: destruct.call:
70-
// REGISTER: call void @bar3()
71-
// REGISTER: br label %unatexit.call
72-
73-
// REGISTER: unatexit.call:
74-
// REGISTER: %1 = call i32 @unatexit(ptr @bar2)
75-
// REGISTER: %needs_destruct1 = icmp eq i32 %1, 0
76-
// REGISTER: br i1 %needs_destruct1, label %destruct.call2, label %destruct.end
77-
78-
// REGISTER: destruct.call2:
7964
// REGISTER: call void @bar2()
8065
// REGISTER: br label %destruct.end
8166

clang/test/CodeGenCXX/aix-destructor-attribute.cpp

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ struct test {
1717
~test();
1818
} t;
1919

20-
int bar() __attribute__((destructor(100)));
20+
int bar() __attribute__((destructor(101)));
2121
int bar2() __attribute__((destructor(65535)));
22-
int bar3(int) __attribute__((destructor(65535)));
2322

2423
int bar() {
2524
return 1;
@@ -29,17 +28,13 @@ int bar2() {
2928
return 2;
3029
}
3130

32-
int bar3(int a) {
33-
return a;
34-
}
35-
3631
// NO-REGISTER: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I__, ptr null }]
37-
// 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 }]
32+
// 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 }]
3833

39-
// 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 }]
40-
// 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 }]
34+
// 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 }]
35+
// 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 }]
4136

42-
// REGISTER: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] {
37+
// REGISTER: define internal void @__GLOBAL_init_101() [[ATTR:#[0-9]+]] {
4338
// REGISTER: entry:
4439
// REGISTER: %0 = call i32 @atexit(ptr @_Z3barv)
4540
// REGISTER: ret void
@@ -48,11 +43,10 @@ int bar3(int a) {
4843
// REGISTER: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] {
4944
// REGISTER: entry:
5045
// REGISTER: %0 = call i32 @atexit(ptr @_Z4bar2v)
51-
// REGISTER: %1 = call i32 @atexit(ptr @_Z4bar3i)
5246
// REGISTER: ret void
5347
// REGISTER: }
5448

55-
// REGISTER: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] {
49+
// REGISTER: define internal void @__GLOBAL_cleanup_101() [[ATTR:#[0-9]+]] {
5650
// REGISTER: entry:
5751
// REGISTER: %0 = call i32 @unatexit(ptr @_Z3barv)
5852
// REGISTER: %needs_destruct = icmp eq i32 %0, 0
@@ -68,20 +62,11 @@ int bar3(int a) {
6862

6963
// REGISTER: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] {
7064
// REGISTER: entry:
71-
// REGISTER: %0 = call i32 @unatexit(ptr @_Z4bar3i)
65+
// REGISTER: %0 = call i32 @unatexit(ptr @_Z4bar2v)
7266
// REGISTER: %needs_destruct = icmp eq i32 %0, 0
73-
// REGISTER: br i1 %needs_destruct, label %destruct.call, label %unatexit.call
67+
// REGISTER: br i1 %needs_destruct, label %destruct.call, label %destruct.end
7468

7569
// REGISTER: destruct.call:
76-
// REGISTER: call void @_Z4bar3i()
77-
// REGISTER: br label %unatexit.call
78-
79-
// REGISTER: unatexit.call:
80-
// REGISTER: %1 = call i32 @unatexit(ptr @_Z4bar2v)
81-
// REGISTER: %needs_destruct1 = icmp eq i32 %1, 0
82-
// REGISTER: br i1 %needs_destruct1, label %destruct.call2, label %destruct.end
83-
84-
// REGISTER: destruct.call2:
8570
// REGISTER: call void @_Z4bar2v()
8671
// REGISTER: br label %destruct.end
8772

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify=err %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify=warn -Wno-error=priority-ctor-dtor %s
3+
// RUN: %clang_cc1 -fsyntax-only -verify=okay -Wno-priority-ctor-dtor %s
4+
// RUN: %clang_cc1 -fsyntax-only -verify=okay -Wno-prio-ctor-dtor %s
5+
// okay-no-diagnostics
6+
7+
void f(void) __attribute__((constructor(1))); // warn-warning {{'constructor' attribute requires integer constant between 101 and 65535 inclusive}} \
8+
err-error {{'constructor' attribute requires integer constant between 101 and 65535 inclusive}}
9+
void f(void) __attribute__((destructor(1))); // warn-warning {{'destructor' attribute requires integer constant between 101 and 65535 inclusive}} \
10+
err-error {{'destructor' attribute requires integer constant between 101 and 65535 inclusive}}

0 commit comments

Comments
 (0)