mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 19:06:44 +00:00
[clang] Ignore GCC 11 [[malloc(x)]] attribute
Ignore the `[[malloc(x)]]` or `[[malloc(x, 1)]]` function attribute syntax added in [GCC 11][1] and print a warning instead of an error. Unlike `[[malloc]]` with no arguments (which is supported by Clang), GCC uses the one or two argument form to specify a deallocator for GCC's static analyzer. Code currently compiled with `[[malloc(x)]]` or `__attribute((malloc(x)))` fails with the following error: `'malloc' attribute takes no arguments`. [1]: https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;f=gcc/doc/extend.texi;h=dce6c58db87ebf7f4477bd3126228e73e4eeee97#patch6 Fixes: https://github.com/llvm/llvm-project/issues/51607 Partial-Bug: https://github.com/llvm/llvm-project/issues/53152
This commit is contained in:
parent
b2aba39001
commit
79a28aa0a4
@ -233,6 +233,11 @@ Bug Fixes to Attribute Support
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- Fixed crash when a parameter to the ``clang::annotate`` attribute evaluates to ``void``. See #GH119125
|
||||
|
||||
- Clang now emits a warning instead of an error when using the one or two
|
||||
argument form of GCC 11's ``__attribute__((malloc(deallocator)))``
|
||||
or ``__attribute__((malloc(deallocator, ptr-index)))``
|
||||
(`#51607 <https://github.com/llvm/llvm-project/issues/51607>`_).
|
||||
|
||||
Bug Fixes to C++ Support
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -1904,6 +1904,8 @@ def IFunc : Attr, TargetSpecificAttr<TargetIFuncSupport> {
|
||||
|
||||
def Restrict : InheritableAttr {
|
||||
let Spellings = [Declspec<"restrict">, GCC<"malloc">];
|
||||
let Args = [ExprArgument<"Deallocator", /*opt=*/ 1>,
|
||||
ParamIdxArgument<"DeallocatorPtrArgIndex", /*opt=*/ 1>];
|
||||
let Subjects = SubjectList<[Function]>;
|
||||
let Documentation = [RestrictDocs];
|
||||
}
|
||||
|
@ -4966,9 +4966,17 @@ def RestrictDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Heading = "malloc";
|
||||
let Content = [{
|
||||
The ``malloc`` attribute indicates that the function acts like a system memory
|
||||
allocation function, returning a pointer to allocated storage disjoint from the
|
||||
storage for any other object accessible to the caller.
|
||||
The ``malloc`` attribute has two forms with different functionality. The first
|
||||
is when it is used without arguments, where it marks that a function acts like
|
||||
a system memory allocation function, returning a pointer to allocated storage
|
||||
that does not alias storage from any other object accessible to the caller.
|
||||
|
||||
The second form is when ``malloc`` takes one or two arguments. The first
|
||||
argument names a function that should be associated with this function as its
|
||||
deallocation function. When this form is used, it enables the compiler to
|
||||
diagnose when the incorrect deallocation function is used with this variable.
|
||||
However the associated warning, spelled `-Wmismatched-dealloc` in GCC, is not
|
||||
yet implemented in clang.
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -3155,6 +3155,10 @@ def warn_auto_var_is_id : Warning<
|
||||
InGroup<DiagGroup<"auto-var-id">>;
|
||||
|
||||
// Attributes
|
||||
def warn_attribute_form_ignored : Warning<
|
||||
"%0 attribute ignored because Clang does not yet support this attribute signature">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
|
||||
def warn_attribute_ignored_no_calls_in_stmt: Warning<
|
||||
"%0 attribute is ignored because there exists no call expression inside the "
|
||||
"statement">,
|
||||
@ -4528,6 +4532,12 @@ def err_attribute_cleanup_func_must_take_one_arg : Error<
|
||||
def err_attribute_cleanup_func_arg_incompatible_type : Error<
|
||||
"'cleanup' function %0 parameter has "
|
||||
"%diff{type $ which is incompatible with type $|incompatible type}1,2">;
|
||||
def err_attribute_malloc_arg_not_function : Error<
|
||||
"'malloc' argument %select{for deallocator |%1 |%1 }0is not a %select{||single }0function">;
|
||||
def err_attribute_malloc_arg_not_function_with_pointer_arg : Error<
|
||||
"'malloc' argument %0 must take a pointer type as its first argument">;
|
||||
def err_attribute_malloc_arg_refers_to_non_pointer_type : Error<
|
||||
"'malloc' argument '%0' refers to non-pointer type %1 of %2">; // in GCC, this is a warning, not an error
|
||||
def err_attribute_regparm_wrong_platform : Error<
|
||||
"'regparm' is not valid on this platform">;
|
||||
def err_attribute_regparm_invalid_number : Error<
|
||||
|
@ -2434,7 +2434,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
|
||||
FuncAttrs.addMemoryAttr(llvm::MemoryEffects::inaccessibleOrArgMemOnly());
|
||||
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
|
||||
}
|
||||
if (TargetDecl->hasAttr<RestrictAttr>())
|
||||
if (const auto *RA = TargetDecl->getAttr<RestrictAttr>();
|
||||
RA && RA->getDeallocator() == nullptr)
|
||||
RetAttrs.addAttribute(llvm::Attribute::NoAlias);
|
||||
if (TargetDecl->hasAttr<ReturnsNonNullAttr>() &&
|
||||
!CodeGenOpts.NullPointerIsValid)
|
||||
|
@ -1780,13 +1780,89 @@ static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
|
||||
static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
QualType ResultType = getFunctionOrMethodResultType(D);
|
||||
if (ResultType->isAnyPointerType() || ResultType->isBlockPointerType()) {
|
||||
if (!ResultType->isAnyPointerType() && !ResultType->isBlockPointerType()) {
|
||||
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
|
||||
<< AL << getFunctionOrMethodResultSourceRange(D);
|
||||
return;
|
||||
}
|
||||
|
||||
if (AL.getNumArgs() == 0) {
|
||||
D->addAttr(::new (S.Context) RestrictAttr(S.Context, AL));
|
||||
return;
|
||||
}
|
||||
|
||||
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
|
||||
<< AL << getFunctionOrMethodResultSourceRange(D);
|
||||
if (AL.getAttributeSpellingListIndex() == RestrictAttr::Declspec_restrict) {
|
||||
// __declspec(restrict) accepts no arguments
|
||||
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// [[gnu::malloc(deallocator)]] with args specifies a deallocator function
|
||||
Expr *DeallocE = AL.getArgAsExpr(0);
|
||||
SourceLocation DeallocLoc = DeallocE->getExprLoc();
|
||||
FunctionDecl *DeallocFD = nullptr;
|
||||
DeclarationNameInfo DeallocNI;
|
||||
|
||||
if (auto *DRE = dyn_cast<DeclRefExpr>(DeallocE)) {
|
||||
DeallocFD = dyn_cast<FunctionDecl>(DRE->getDecl());
|
||||
DeallocNI = DRE->getNameInfo();
|
||||
if (!DeallocFD) {
|
||||
S.Diag(DeallocLoc, diag::err_attribute_malloc_arg_not_function)
|
||||
<< 1 << DeallocNI.getName();
|
||||
return;
|
||||
}
|
||||
} else if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(DeallocE)) {
|
||||
DeallocFD = S.ResolveSingleFunctionTemplateSpecialization(ULE, true);
|
||||
DeallocNI = ULE->getNameInfo();
|
||||
if (!DeallocFD) {
|
||||
S.Diag(DeallocLoc, diag::err_attribute_malloc_arg_not_function)
|
||||
<< 2 << DeallocNI.getName();
|
||||
if (ULE->getType() == S.Context.OverloadTy)
|
||||
S.NoteAllOverloadCandidates(ULE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
S.Diag(DeallocLoc, diag::err_attribute_malloc_arg_not_function) << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2nd arg of [[gnu::malloc(deallocator, 2)]] with args specifies the param
|
||||
// of deallocator that deallocates the pointer (defaults to 1)
|
||||
ParamIdx DeallocPtrIdx;
|
||||
if (AL.getNumArgs() == 1) {
|
||||
DeallocPtrIdx = ParamIdx(1, DeallocFD);
|
||||
|
||||
if (!DeallocPtrIdx.isValid() ||
|
||||
!getFunctionOrMethodParamType(DeallocFD, DeallocPtrIdx.getASTIndex())
|
||||
.getCanonicalType()
|
||||
->isPointerType()) {
|
||||
S.Diag(DeallocLoc,
|
||||
diag::err_attribute_malloc_arg_not_function_with_pointer_arg)
|
||||
<< DeallocNI.getName();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!S.checkFunctionOrMethodParameterIndex(
|
||||
DeallocFD, AL, 2, AL.getArgAsExpr(1), DeallocPtrIdx,
|
||||
/* CanIndexImplicitThis=*/false))
|
||||
return;
|
||||
|
||||
QualType DeallocPtrArgType =
|
||||
getFunctionOrMethodParamType(DeallocFD, DeallocPtrIdx.getASTIndex());
|
||||
if (!DeallocPtrArgType.getCanonicalType()->isPointerType()) {
|
||||
S.Diag(DeallocLoc,
|
||||
diag::err_attribute_malloc_arg_refers_to_non_pointer_type)
|
||||
<< DeallocPtrIdx.getSourceIndex() << DeallocPtrArgType
|
||||
<< DeallocNI.getName();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: we should add this attribute to Clang's AST, so that clang-analyzer
|
||||
// can use it, see -Wmismatched-dealloc in GCC for what we can do with this.
|
||||
S.Diag(AL.getLoc(), diag::warn_attribute_form_ignored) << AL;
|
||||
D->addAttr(::new (S.Context)
|
||||
RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
|
||||
}
|
||||
|
||||
static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
|
12
clang/test/CodeGen/attr-malloc.c
Normal file
12
clang/test/CodeGen/attr-malloc.c
Normal file
@ -0,0 +1,12 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
|
||||
|
||||
int *Mem;
|
||||
void dealloc(int*);
|
||||
|
||||
__attribute__((malloc)) int *MallocFunc(){ return Mem;}
|
||||
// CHECK: define[[BEFORE:.*]] noalias[[AFTER:.*]]@MallocFunc
|
||||
// Ensure these two do not generate noalias here.
|
||||
__attribute__((malloc(dealloc))) int *MallocFunc2(){ return Mem;}
|
||||
// CHECK: define[[BEFORE]][[AFTER]]@MallocFunc2
|
||||
__attribute__((malloc(dealloc, 1))) int *MallocFunc3(){ return Mem;}
|
||||
// CHECK: define[[BEFORE]][[AFTER]]@MallocFunc3
|
@ -1,12 +1,24 @@
|
||||
// RUN: %clang_cc1 -verify -Wunused -Wused-but-marked-unused -Wunused-parameter -fsyntax-only %s
|
||||
// RUN: %clang_cc1 -verify -Wunused -Wused-but-marked-unused -Wunused-parameter -fsyntax-only -fdeclspec %s
|
||||
int a;
|
||||
void func_a(void * ptr, int a);
|
||||
void func_b(int a);
|
||||
void __attribute__((overloadable)) ambigious_func(void *); // expected-note {{candidate function}}
|
||||
void __attribute__((overloadable)) ambigious_func(void *, int); // expected-note {{candidate function}}
|
||||
|
||||
inline __attribute__((noreturn(a))) void *f1(void); // expected-error {{'noreturn' attribute takes no arguments}}
|
||||
inline __attribute__((always_inline(a))) void *f2(void); // expected-error {{'always_inline' attribute takes no arguments}}
|
||||
inline __attribute__((cdecl(a))) void *f3(void); // expected-error {{'cdecl' attribute takes no arguments}}
|
||||
inline __attribute__((const(a))) void *f4(void); // expected-error {{'const' attribute takes no arguments}}
|
||||
inline __attribute__((fastcall(a))) void *f5(void); // expected-error {{'fastcall' attribute takes no arguments}}
|
||||
inline __attribute__((malloc(a))) void *f5(void); // expected-error {{'malloc' attribute takes no arguments}}
|
||||
inline __declspec(restrict(a)) void *f6_a(void); // expected-error {{'restrict' attribute takes no arguments}}
|
||||
inline __attribute__((malloc(func_a, 1, a))) void *f6_b(void); // expected-error {{'malloc' attribute takes no more than 2 arguments}}
|
||||
inline __attribute__((malloc(func_a, 1))) void *f6_c(void); // expected-warning {{'malloc' attribute ignored because Clang does not yet support this attribute signature}}
|
||||
inline __attribute__((malloc(1234))) void *f6_d(void); // expected-error {{'malloc' argument for deallocator is not a function}}
|
||||
inline __attribute__((malloc(a))) void *f6_e(void); // expected-error {{'malloc' argument 'a' is not a function}}
|
||||
inline __attribute__((malloc(ambigious_func))) void *f6_f(void); // expected-error {{'malloc' argument 'ambigious_func' is not a single function}}
|
||||
inline __attribute__((malloc(func_b))) void *f6_g(void); // expected-error {{'malloc' argument 'func_b' must take a pointer type as its first argument}}
|
||||
inline __attribute__((malloc(func_a, 3))) void *f6_h(void); // expected-error {{'malloc' attribute parameter 2 is out of bounds}}
|
||||
inline __attribute__((malloc(func_a, 2))) void *f6_i(void); // expected-error {{'malloc' argument '2' refers to non-pointer type 'int' of 'func_a'}}
|
||||
inline __attribute__((nothrow(a))) void *f7(void); // expected-error {{'nothrow' attribute takes no arguments}}
|
||||
inline __attribute__((stdcall(a))) void *f8(void); // expected-error {{'stdcall' attribute takes no arguments}}
|
||||
inline __attribute__((used(a))) void *f9(void); // expected-error {{'used' attribute takes no arguments}}
|
||||
|
@ -28,6 +28,14 @@ int v __attribute__((visibility("hidden")));
|
||||
// CHECK: char *PR24565() __attribute__((malloc))
|
||||
char *PR24565() __attribute__((__malloc__));
|
||||
|
||||
void my_cleanup_func(char *);
|
||||
|
||||
// using __attribute__(malloc()) with args is currently ignored by Clang
|
||||
// CHECK: char *PR52265_a()
|
||||
__attribute__((malloc(my_cleanup_func))) char *PR52265_a();
|
||||
// CHECK: char *PR52265_b()
|
||||
__attribute__((malloc(my_cleanup_func, 1))) char *PR52265_b();
|
||||
|
||||
// CHECK: class __attribute__((consumable("unknown"))) AttrTester1
|
||||
class __attribute__((consumable(unknown))) AttrTester1 {
|
||||
// CHECK: void callableWhen() __attribute__((callable_when("unconsumed", "consumed")));
|
||||
|
Loading…
x
Reference in New Issue
Block a user