Skip to content

Commit 79a28aa

Browse files
aloisklinkerichkeane
authored andcommitted
[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: #51607 Partial-Bug: #53152
1 parent b2aba39 commit 79a28aa

File tree

9 files changed

+143
-9
lines changed

9 files changed

+143
-9
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ Bug Fixes to Attribute Support
233233
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
234234
- Fixed crash when a parameter to the ``clang::annotate`` attribute evaluates to ``void``. See #GH119125
235235

236+
- Clang now emits a warning instead of an error when using the one or two
237+
argument form of GCC 11's ``__attribute__((malloc(deallocator)))``
238+
or ``__attribute__((malloc(deallocator, ptr-index)))``
239+
(`#51607 <https://github.com/llvm/llvm-project/issues/51607>`_).
240+
236241
Bug Fixes to C++ Support
237242
^^^^^^^^^^^^^^^^^^^^^^^^
238243

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,8 @@ def IFunc : Attr, TargetSpecificAttr<TargetIFuncSupport> {
19041904

19051905
def Restrict : InheritableAttr {
19061906
let Spellings = [Declspec<"restrict">, GCC<"malloc">];
1907+
let Args = [ExprArgument<"Deallocator", /*opt=*/ 1>,
1908+
ParamIdxArgument<"DeallocatorPtrArgIndex", /*opt=*/ 1>];
19071909
let Subjects = SubjectList<[Function]>;
19081910
let Documentation = [RestrictDocs];
19091911
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4966,9 +4966,17 @@ def RestrictDocs : Documentation {
49664966
let Category = DocCatFunction;
49674967
let Heading = "malloc";
49684968
let Content = [{
4969-
The ``malloc`` attribute indicates that the function acts like a system memory
4970-
allocation function, returning a pointer to allocated storage disjoint from the
4971-
storage for any other object accessible to the caller.
4969+
The ``malloc`` attribute has two forms with different functionality. The first
4970+
is when it is used without arguments, where it marks that a function acts like
4971+
a system memory allocation function, returning a pointer to allocated storage
4972+
that does not alias storage from any other object accessible to the caller.
4973+
4974+
The second form is when ``malloc`` takes one or two arguments. The first
4975+
argument names a function that should be associated with this function as its
4976+
deallocation function. When this form is used, it enables the compiler to
4977+
diagnose when the incorrect deallocation function is used with this variable.
4978+
However the associated warning, spelled `-Wmismatched-dealloc` in GCC, is not
4979+
yet implemented in clang.
49724980
}];
49734981
}
49744982

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3155,6 +3155,10 @@ def warn_auto_var_is_id : Warning<
31553155
InGroup<DiagGroup<"auto-var-id">>;
31563156

31573157
// Attributes
3158+
def warn_attribute_form_ignored : Warning<
3159+
"%0 attribute ignored because Clang does not yet support this attribute signature">,
3160+
InGroup<IgnoredAttributes>;
3161+
31583162
def warn_attribute_ignored_no_calls_in_stmt: Warning<
31593163
"%0 attribute is ignored because there exists no call expression inside the "
31603164
"statement">,
@@ -4528,6 +4532,12 @@ def err_attribute_cleanup_func_must_take_one_arg : Error<
45284532
def err_attribute_cleanup_func_arg_incompatible_type : Error<
45294533
"'cleanup' function %0 parameter has "
45304534
"%diff{type $ which is incompatible with type $|incompatible type}1,2">;
4535+
def err_attribute_malloc_arg_not_function : Error<
4536+
"'malloc' argument %select{for deallocator |%1 |%1 }0is not a %select{||single }0function">;
4537+
def err_attribute_malloc_arg_not_function_with_pointer_arg : Error<
4538+
"'malloc' argument %0 must take a pointer type as its first argument">;
4539+
def err_attribute_malloc_arg_refers_to_non_pointer_type : Error<
4540+
"'malloc' argument '%0' refers to non-pointer type %1 of %2">; // in GCC, this is a warning, not an error
45314541
def err_attribute_regparm_wrong_platform : Error<
45324542
"'regparm' is not valid on this platform">;
45334543
def err_attribute_regparm_invalid_number : Error<

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2434,7 +2434,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
24342434
FuncAttrs.addMemoryAttr(llvm::MemoryEffects::inaccessibleOrArgMemOnly());
24352435
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
24362436
}
2437-
if (TargetDecl->hasAttr<RestrictAttr>())
2437+
if (const auto *RA = TargetDecl->getAttr<RestrictAttr>();
2438+
RA && RA->getDeallocator() == nullptr)
24382439
RetAttrs.addAttribute(llvm::Attribute::NoAlias);
24392440
if (TargetDecl->hasAttr<ReturnsNonNullAttr>() &&
24402441
!CodeGenOpts.NullPointerIsValid)

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,13 +1780,89 @@ static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
17801780

17811781
static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
17821782
QualType ResultType = getFunctionOrMethodResultType(D);
1783-
if (ResultType->isAnyPointerType() || ResultType->isBlockPointerType()) {
1783+
if (!ResultType->isAnyPointerType() && !ResultType->isBlockPointerType()) {
1784+
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
1785+
<< AL << getFunctionOrMethodResultSourceRange(D);
1786+
return;
1787+
}
1788+
1789+
if (AL.getNumArgs() == 0) {
17841790
D->addAttr(::new (S.Context) RestrictAttr(S.Context, AL));
17851791
return;
17861792
}
17871793

1788-
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
1789-
<< AL << getFunctionOrMethodResultSourceRange(D);
1794+
if (AL.getAttributeSpellingListIndex() == RestrictAttr::Declspec_restrict) {
1795+
// __declspec(restrict) accepts no arguments
1796+
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 0;
1797+
return;
1798+
}
1799+
1800+
// [[gnu::malloc(deallocator)]] with args specifies a deallocator function
1801+
Expr *DeallocE = AL.getArgAsExpr(0);
1802+
SourceLocation DeallocLoc = DeallocE->getExprLoc();
1803+
FunctionDecl *DeallocFD = nullptr;
1804+
DeclarationNameInfo DeallocNI;
1805+
1806+
if (auto *DRE = dyn_cast<DeclRefExpr>(DeallocE)) {
1807+
DeallocFD = dyn_cast<FunctionDecl>(DRE->getDecl());
1808+
DeallocNI = DRE->getNameInfo();
1809+
if (!DeallocFD) {
1810+
S.Diag(DeallocLoc, diag::err_attribute_malloc_arg_not_function)
1811+
<< 1 << DeallocNI.getName();
1812+
return;
1813+
}
1814+
} else if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(DeallocE)) {
1815+
DeallocFD = S.ResolveSingleFunctionTemplateSpecialization(ULE, true);
1816+
DeallocNI = ULE->getNameInfo();
1817+
if (!DeallocFD) {
1818+
S.Diag(DeallocLoc, diag::err_attribute_malloc_arg_not_function)
1819+
<< 2 << DeallocNI.getName();
1820+
if (ULE->getType() == S.Context.OverloadTy)
1821+
S.NoteAllOverloadCandidates(ULE);
1822+
return;
1823+
}
1824+
} else {
1825+
S.Diag(DeallocLoc, diag::err_attribute_malloc_arg_not_function) << 0;
1826+
return;
1827+
}
1828+
1829+
// 2nd arg of [[gnu::malloc(deallocator, 2)]] with args specifies the param
1830+
// of deallocator that deallocates the pointer (defaults to 1)
1831+
ParamIdx DeallocPtrIdx;
1832+
if (AL.getNumArgs() == 1) {
1833+
DeallocPtrIdx = ParamIdx(1, DeallocFD);
1834+
1835+
if (!DeallocPtrIdx.isValid() ||
1836+
!getFunctionOrMethodParamType(DeallocFD, DeallocPtrIdx.getASTIndex())
1837+
.getCanonicalType()
1838+
->isPointerType()) {
1839+
S.Diag(DeallocLoc,
1840+
diag::err_attribute_malloc_arg_not_function_with_pointer_arg)
1841+
<< DeallocNI.getName();
1842+
return;
1843+
}
1844+
} else {
1845+
if (!S.checkFunctionOrMethodParameterIndex(
1846+
DeallocFD, AL, 2, AL.getArgAsExpr(1), DeallocPtrIdx,
1847+
/* CanIndexImplicitThis=*/false))
1848+
return;
1849+
1850+
QualType DeallocPtrArgType =
1851+
getFunctionOrMethodParamType(DeallocFD, DeallocPtrIdx.getASTIndex());
1852+
if (!DeallocPtrArgType.getCanonicalType()->isPointerType()) {
1853+
S.Diag(DeallocLoc,
1854+
diag::err_attribute_malloc_arg_refers_to_non_pointer_type)
1855+
<< DeallocPtrIdx.getSourceIndex() << DeallocPtrArgType
1856+
<< DeallocNI.getName();
1857+
return;
1858+
}
1859+
}
1860+
1861+
// FIXME: we should add this attribute to Clang's AST, so that clang-analyzer
1862+
// can use it, see -Wmismatched-dealloc in GCC for what we can do with this.
1863+
S.Diag(AL.getLoc(), diag::warn_attribute_form_ignored) << AL;
1864+
D->addAttr(::new (S.Context)
1865+
RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
17901866
}
17911867

17921868
static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {

clang/test/CodeGen/attr-malloc.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
2+
3+
int *Mem;
4+
void dealloc(int*);
5+
6+
__attribute__((malloc)) int *MallocFunc(){ return Mem;}
7+
// CHECK: define[[BEFORE:.*]] noalias[[AFTER:.*]]@MallocFunc
8+
// Ensure these two do not generate noalias here.
9+
__attribute__((malloc(dealloc))) int *MallocFunc2(){ return Mem;}
10+
// CHECK: define[[BEFORE]][[AFTER]]@MallocFunc2
11+
__attribute__((malloc(dealloc, 1))) int *MallocFunc3(){ return Mem;}
12+
// CHECK: define[[BEFORE]][[AFTER]]@MallocFunc3

clang/test/Sema/attr-args.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
// RUN: %clang_cc1 -verify -Wunused -Wused-but-marked-unused -Wunused-parameter -fsyntax-only %s
1+
// RUN: %clang_cc1 -verify -Wunused -Wused-but-marked-unused -Wunused-parameter -fsyntax-only -fdeclspec %s
22
int a;
3+
void func_a(void * ptr, int a);
4+
void func_b(int a);
5+
void __attribute__((overloadable)) ambigious_func(void *); // expected-note {{candidate function}}
6+
void __attribute__((overloadable)) ambigious_func(void *, int); // expected-note {{candidate function}}
37

48
inline __attribute__((noreturn(a))) void *f1(void); // expected-error {{'noreturn' attribute takes no arguments}}
59
inline __attribute__((always_inline(a))) void *f2(void); // expected-error {{'always_inline' attribute takes no arguments}}
610
inline __attribute__((cdecl(a))) void *f3(void); // expected-error {{'cdecl' attribute takes no arguments}}
711
inline __attribute__((const(a))) void *f4(void); // expected-error {{'const' attribute takes no arguments}}
812
inline __attribute__((fastcall(a))) void *f5(void); // expected-error {{'fastcall' attribute takes no arguments}}
9-
inline __attribute__((malloc(a))) void *f5(void); // expected-error {{'malloc' attribute takes no arguments}}
13+
inline __declspec(restrict(a)) void *f6_a(void); // expected-error {{'restrict' attribute takes no arguments}}
14+
inline __attribute__((malloc(func_a, 1, a))) void *f6_b(void); // expected-error {{'malloc' attribute takes no more than 2 arguments}}
15+
inline __attribute__((malloc(func_a, 1))) void *f6_c(void); // expected-warning {{'malloc' attribute ignored because Clang does not yet support this attribute signature}}
16+
inline __attribute__((malloc(1234))) void *f6_d(void); // expected-error {{'malloc' argument for deallocator is not a function}}
17+
inline __attribute__((malloc(a))) void *f6_e(void); // expected-error {{'malloc' argument 'a' is not a function}}
18+
inline __attribute__((malloc(ambigious_func))) void *f6_f(void); // expected-error {{'malloc' argument 'ambigious_func' is not a single function}}
19+
inline __attribute__((malloc(func_b))) void *f6_g(void); // expected-error {{'malloc' argument 'func_b' must take a pointer type as its first argument}}
20+
inline __attribute__((malloc(func_a, 3))) void *f6_h(void); // expected-error {{'malloc' attribute parameter 2 is out of bounds}}
21+
inline __attribute__((malloc(func_a, 2))) void *f6_i(void); // expected-error {{'malloc' argument '2' refers to non-pointer type 'int' of 'func_a'}}
1022
inline __attribute__((nothrow(a))) void *f7(void); // expected-error {{'nothrow' attribute takes no arguments}}
1123
inline __attribute__((stdcall(a))) void *f8(void); // expected-error {{'stdcall' attribute takes no arguments}}
1224
inline __attribute__((used(a))) void *f9(void); // expected-error {{'used' attribute takes no arguments}}

clang/test/SemaCXX/attr-print.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ int v __attribute__((visibility("hidden")));
2828
// CHECK: char *PR24565() __attribute__((malloc))
2929
char *PR24565() __attribute__((__malloc__));
3030

31+
void my_cleanup_func(char *);
32+
33+
// using __attribute__(malloc()) with args is currently ignored by Clang
34+
// CHECK: char *PR52265_a()
35+
__attribute__((malloc(my_cleanup_func))) char *PR52265_a();
36+
// CHECK: char *PR52265_b()
37+
__attribute__((malloc(my_cleanup_func, 1))) char *PR52265_b();
38+
3139
// CHECK: class __attribute__((consumable("unknown"))) AttrTester1
3240
class __attribute__((consumable(unknown))) AttrTester1 {
3341
// CHECK: void callableWhen() __attribute__((callable_when("unconsumed", "consumed")));

0 commit comments

Comments
 (0)