Skip to content

Commit

Permalink
fixup! [clang] Ignore GCC 11 [[malloc(x)]] attribute
Browse files Browse the repository at this point in the history
Error if `deallocator` and `ptrIdx` to
`[[malloc(deallocator, ptrIdx)]]` have invalid types.
  • Loading branch information
aloisklink committed Nov 1, 2023
1 parent 5f6f893 commit 0659620
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 7 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/Attr.td
Expand Up @@ -1629,7 +1629,7 @@ def IFunc : Attr, TargetSpecificAttr<TargetELF> {

def Restrict : InheritableAttr {
let Spellings = [Declspec<"restrict">, GCC<"malloc">];
let Args = [IdentifierArgument<"Deallocator", /*opt=*/ 1>,
let Args = [ExprArgument<"Deallocator", /*opt=*/ 1>,
ParamIdxArgument<"DeallocatorPtrArgIndex", /*opt=*/ 1>];
let Subjects = SubjectList<[Function]>;
let Documentation = [RestrictDocs];
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -4252,6 +4252,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<
Expand Down
68 changes: 64 additions & 4 deletions clang/lib/Sema/SemaDeclAttr.cpp
Expand Up @@ -2070,7 +2070,7 @@ static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
return;
}

if (getNumAttributeArgs(AL) == 0) {
if (AL.getNumArgs() == 0) {
D->addAttr(::new (S.Context) RestrictAttr(S.Context, AL));
return;
}
Expand All @@ -2081,9 +2081,69 @@ static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
return;
}

// FIXME: GCC uses [[malloc(my_func)]] to specify a deallocator for the
// returned pointer, but this isn't currently supported in LLVM
// see https://github.com/llvm/llvm-project/issues/51607
// [[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 (!checkFunctionOrMethodParameterIndex(S, 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.
S.Diag(AL.getLoc(), diag::warn_attribute_form_ignored) << AL;
}

Expand Down
14 changes: 12 additions & 2 deletions clang/test/Sema/attr-args.c
@@ -1,14 +1,24 @@
// 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 __declspec(restrict(a)) void *f6_a(void); // expected-error {{'restrict' attribute takes no arguments}}
inline __attribute__((malloc(a, 1, a))) void *f6_b(void); // expected-error {{'malloc' attribute takes no more than 2 arguments}}
inline __attribute__((malloc(a, 1))) void *f6_c(void); // expected-warning {{'malloc' attribute ignored because Clang does not yet support this attribute signature}}
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}}
Expand Down

0 comments on commit 0659620

Please sign in to comment.