-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Diagnose problematic uses of constructor/destructor attribute #67360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
6e9bcfa
5c9509d
d3666f2
327bb85
2b99083
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2864,6 +2864,8 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning< | |
InGroup<CXXPre14Compat>, DefaultIgnore; | ||
def note_constexpr_body_previous_return : Note< | ||
"previous return statement is here">; | ||
def err_ctordtor_attr_consteval : Error< | ||
"%0 attribute cannot be applied to a 'consteval' function">; | ||
|
||
// C++20 function try blocks in constexpr | ||
def ext_constexpr_function_try_block_cxx20 : ExtWarn< | ||
|
@@ -3176,6 +3178,11 @@ def err_alignas_underaligned : Error< | |
"requested alignment is less than minimum alignment of %1 for type %0">; | ||
def warn_aligned_attr_underaligned : Warning<err_alignas_underaligned.Summary>, | ||
InGroup<IgnoredAttributes>; | ||
def err_ctor_dtor_attr_on_non_void_func : Error< | ||
"%0 attribute can only be applied to a function which accepts no arguments " | ||
"and has a 'void' or 'int' return type">; | ||
Comment on lines
+3181
to
+3183
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC, there's a similar restriction for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not that I could find -- the function marked
I looked around for an existing diagnostic to augment but didn't spot any that worked well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, perhaps what I'm thinking of is that we aught to emit a similar diagnostic for the return type of those functions. |
||
def err_ctor_dtor_member_func : Error< | ||
"%0 attribute cannot be applied to a member function">; | ||
def err_attribute_sizeless_type : Error< | ||
"%0 attribute cannot be applied to sizeless type %1">; | ||
def err_attribute_argument_n_type : Error< | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2352,26 +2352,78 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { | |
D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL)); | ||
} | ||
|
||
static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { | ||
uint32_t priority = ConstructorAttr::DefaultPriority; | ||
static bool diagnoseInvalidPriority(Sema &S, uint32_t Priority, | ||
const ParsedAttr &A, | ||
SourceLocation PriorityLoc) { | ||
constexpr uint32_t ReservedPriorityLower = 101, ReservedPriorityUpper = 65535; | ||
|
||
// Only perform the priority check if the attribute is outside of a system | ||
// header. Values <= 100 are reserved for the implementation, and libc++ | ||
// benefits from being able to specify values in that range. Values > 65535 | ||
// are reserved for historical reasons. | ||
if ((Priority < ReservedPriorityLower || Priority > ReservedPriorityUpper) && | ||
!S.getSourceManager().isInSystemHeader(A.getLoc())) { | ||
S.Diag(A.getLoc(), diag::err_attribute_argument_out_of_range) | ||
<< PriorityLoc << A << ReservedPriorityLower << ReservedPriorityUpper; | ||
A.setInvalid(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
template <typename CtorDtorAttr> | ||
static void handleCtorDtorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { | ||
uint32_t Priority = CtorDtorAttr::DefaultPriority; | ||
if (S.getLangOpts().HLSL && AL.getNumArgs()) { | ||
S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported); | ||
return; | ||
} | ||
if (AL.getNumArgs() && | ||
!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority)) | ||
return; | ||
|
||
D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority)); | ||
} | ||
// If we're given an argument for the priority, check that it's valid. | ||
if (AL.getNumArgs()) { | ||
if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), Priority)) | ||
return; | ||
|
||
static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { | ||
uint32_t priority = DestructorAttr::DefaultPriority; | ||
if (AL.getNumArgs() && | ||
!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority)) | ||
// Ensure the priority is in a reasonable range. | ||
if (diagnoseInvalidPriority(S, Priority, AL, | ||
AL.getArgAsExpr(0)->getExprLoc())) | ||
return; | ||
} | ||
|
||
// Ensure the function we're attaching to is something that is sensible to | ||
// automatically call before or after main(); it should accept no arguments. | ||
// In theory, a void return type is the only truly safe return type (consider | ||
// that calling conventions may place returned values in a hidden pointer | ||
// argument passed to the function that will not be present when called | ||
// automatically). However, there is a significant amount of existing code | ||
// which uses an int return type. So we will accept void, int, and | ||
// unsigned int return types. Any other return type, or a non-void parameter | ||
// list is treated as an error because it's a form of type system | ||
// incompatibility. The function also cannot be a member function. We allow | ||
// K&R C functions because that's a difficult edge case where it depends on | ||
// how the function is defined as to whether it does or does not expect | ||
// arguments. | ||
const auto *FD = cast<FunctionDecl>(D); | ||
QualType RetTy = FD->getReturnType(); | ||
if (!(RetTy->isVoidType() || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it also make sense to check that the function uses the C calling convention and isn't varargs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think those restrictions make sense, good call! |
||
RetTy->isSpecificBuiltinType(BuiltinType::UInt) || | ||
RetTy->isSpecificBuiltinType(BuiltinType::Int)) || | ||
(FD->hasPrototype() && FD->getNumParams() != 0)) { | ||
S.Diag(AL.getLoc(), diag::err_ctor_dtor_attr_on_non_void_func) | ||
<< AL << FD->getSourceRange(); | ||
return; | ||
} else if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); | ||
MD && MD->isInstance()) { | ||
S.Diag(AL.getLoc(), diag::err_ctor_dtor_member_func) | ||
<< AL << FD->getSourceRange(); | ||
return; | ||
} else if (FD->isConsteval()) { | ||
S.Diag(AL.getLoc(), diag::err_ctordtor_attr_consteval) | ||
<< AL << FD->getSourceRange(); | ||
return; | ||
} | ||
|
||
D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority)); | ||
D->addAttr(CtorDtorAttr::Create(S.Context, Priority, AL)); | ||
} | ||
|
||
template <typename AttrTy> | ||
|
@@ -3888,16 +3940,9 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { | |
return; | ||
} | ||
|
||
// Only perform the priority check if the attribute is outside of a system | ||
// header. Values <= 100 are reserved for the implementation, and libc++ | ||
// benefits from being able to specify values in that range. | ||
if ((prioritynum < 101 || prioritynum > 65535) && | ||
!S.getSourceManager().isInSystemHeader(AL.getLoc())) { | ||
S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_range) | ||
<< E->getSourceRange() << AL << 101 << 65535; | ||
AL.setInvalid(); | ||
if (diagnoseInvalidPriority(S, prioritynum, AL, E->getExprLoc())) | ||
return; | ||
} | ||
|
||
D->addAttr(::new (S.Context) InitPriorityAttr(S.Context, AL, prioritynum)); | ||
} | ||
|
||
|
@@ -8930,13 +8975,13 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, | |
handlePassObjectSizeAttr(S, D, AL); | ||
break; | ||
case ParsedAttr::AT_Constructor: | ||
handleConstructorAttr(S, D, AL); | ||
handleCtorDtorAttr<ConstructorAttr>(S, D, AL); | ||
break; | ||
case ParsedAttr::AT_Deprecated: | ||
handleDeprecatedAttr(S, D, AL); | ||
break; | ||
case ParsedAttr::AT_Destructor: | ||
handleDestructorAttr(S, D, AL); | ||
handleCtorDtorAttr<DestructorAttr>(S, D, AL); | ||
break; | ||
case ParsedAttr::AT_EnableIf: | ||
handleEnableIfAttr(S, D, AL); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,4 @@ | ||
// RUN: %clang_cc1 %s -emit-llvm -o - | grep llvm.global_ctors | ||
int __attribute__((constructor)) foo(void) { | ||
return 0; | ||
} | ||
void __attribute__((constructor)) foo(void) {} | ||
void __attribute__((constructor)) bar(void) {} | ||
|
Uh oh!
There was an error while loading. Please reload this page.