diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1308,7 +1308,7 @@ def AllocSize : InheritableAttr { let Spellings = [GCC<"alloc_size">]; - let Subjects = SubjectList<[Function]>; + let Subjects = SubjectList<[HasFunctionProto]>; let Args = [ParamIdxArgument<"ElemSizeParam">, ParamIdxArgument<"NumElemsParam", /*opt*/ 1>]; let TemplateDependent = 1; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -108,8 +108,11 @@ /// Given a CallExpr, try to get the alloc_size attribute. May return null. static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) { - const FunctionDecl *Callee = CE->getDirectCallee(); - return Callee ? Callee->getAttr() : nullptr; + if (const FunctionDecl *DirectCallee = CE->getDirectCallee()) + return DirectCallee->getAttr(); + if (const Decl *IndirectCallee = CE->getCalleeDecl()) + return IndirectCallee->getAttr(); + return nullptr; } /// Attempts to unwrap a CallExpr (with an alloc_size attribute) from an Expr. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6831,6 +6831,16 @@ return true; } +template +static void copyAttrFromTypedefToDecl(Sema &S, Decl *D, const TypedefType *TT) { + const TypedefNameDecl *TND = TT->getDecl(); + if (const auto *Attribute = TND->getAttr()) { + AttrTy *Clone = Attribute->clone(S.Context); + Clone->setInherited(true); + D->addAttr(Clone); + } +} + NamedDecl *Sema::ActOnVariableDeclarator( Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, @@ -7253,6 +7263,14 @@ // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); + // FIXME: This is probably the wrong location to be doing this and we should + // probably be doing this for more attributes (especially for function + // pointer attributes such as format, warn_unused_result, etc.). Ideally + // the code to copy attributes would be generated by TableGen. + if (R->isFunctionPointerType()) + if (const auto *TT = R->getAs()) + copyAttrFromTypedefToDecl(*this, NewVD, TT); + if (getLangOpts().CUDA || getLangOpts().OpenMPIsDevice || getLangOpts().SYCLIsDevice) { if (EmitTLSUnsupportedError && diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -788,20 +788,20 @@ /// /// AttrArgNo is used to actually retrieve the argument, so it's base-0. template -static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD, - const AttrInfo &AI, unsigned AttrArgNo) { +static bool checkParamIsIntegerType(Sema &S, const Decl *D, const AttrInfo &AI, + unsigned AttrArgNo) { assert(AI.isArgExpr(AttrArgNo) && "Expected expression argument"); Expr *AttrArg = AI.getArgAsExpr(AttrArgNo); ParamIdx Idx; - if (!checkFunctionOrMethodParameterIndex(S, FD, AI, AttrArgNo + 1, AttrArg, + if (!checkFunctionOrMethodParameterIndex(S, D, AI, AttrArgNo + 1, AttrArg, Idx)) return false; - const ParmVarDecl *Param = FD->getParamDecl(Idx.getASTIndex()); - if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) { + QualType ParamTy = getFunctionOrMethodParamType(D, Idx.getASTIndex()); + if (!ParamTy->isIntegerType() && !ParamTy->isCharType()) { SourceLocation SrcLoc = AttrArg->getBeginLoc(); S.Diag(SrcLoc, diag::err_attribute_integers_only) - << AI << Param->getSourceRange(); + << AI << getFunctionOrMethodParamRange(D, Idx.getASTIndex()); return false; } return true; @@ -811,8 +811,10 @@ if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 2)) return; - const auto *FD = cast(D); - if (!FD->getReturnType()->isPointerType()) { + assert(isFunctionOrMethod(D) && hasFunctionProto(D)); + + QualType RetTy = getFunctionOrMethodResultType(D); + if (!RetTy->isPointerType()) { S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) << AL; return; } @@ -822,7 +824,7 @@ // Parameter indices are 1-indexed, hence Index=1 if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNoVal, /*Idx=*/1)) return; - if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/0)) + if (!checkParamIsIntegerType(S, D, AL, /*AttrArgNo=*/0)) return; ParamIdx SizeArgNo(SizeArgNoVal, D); @@ -833,7 +835,7 @@ // Parameter indices are 1-based, hence Index=2 if (!checkPositiveIntArgument(S, AL, NumberExpr, Val, /*Idx=*/2)) return; - if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/1)) + if (!checkParamIsIntegerType(S, D, AL, /*AttrArgNo=*/1)) return; NumberArgNo = ParamIdx(Val, D); } diff --git a/clang/test/CodeGen/alloc-size-fnptr.c b/clang/test/CodeGen/alloc-size-fnptr.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/alloc-size-fnptr.c @@ -0,0 +1,55 @@ +// Check that the alloc_size attribute is propagated to the call instruction +// for both direct and indirect calls +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck %s + +#define NULL ((void *)0) + +int gi; + +typedef unsigned long size_t; + +extern void *my_malloc(int) __attribute__((alloc_size(1))); +extern void *my_calloc(int, int) __attribute__((alloc_size(1, 2))); + +// CHECK-LABEL: @call_direct +void call_direct(void) { + my_malloc(50); + // CHECK: call i8* @my_malloc(i32 50) [[DIRECT_MALLOC_ATTR:#[0-9]+]] + my_calloc(1, 16); + // CHECK: call i8* @my_calloc(i32 1, i32 16) [[DIRECT_CALLOC_ATTR:#[0-9]+]] +} + +extern void *(*malloc_function_pointer)(void *, int)__attribute__((alloc_size(2))); +extern void *(*calloc_function_pointer)(void *, int, int)__attribute__((alloc_size(2, 3))); + +// CHECK-LABEL: @call_function_pointer +void call_function_pointer(void) { + malloc_function_pointer(NULL, 100); + // CHECK: [[MALLOC_FN_PTR:%.+]] = load i8* (i8*, i32)*, i8* (i8*, i32)** @malloc_function_pointer, align 8 + // CHECK: call i8* [[MALLOC_FN_PTR]](i8* null, i32 100) [[INDIRECT_MALLOC_ATTR:#[0-9]+]] + calloc_function_pointer(NULL, 2, 4); + // CHECK: [[CALLOC_FN_PTR:%.+]] = load i8* (i8*, i32, i32)*, i8* (i8*, i32, i32)** @calloc_function_pointer, align 8 + // CHECK: call i8* [[CALLOC_FN_PTR]](i8* null, i32 2, i32 4) [[INDIRECT_CALLOC_ATTR:#[0-9]+]] +} + +typedef void *(__attribute__((alloc_size(3))) * my_malloc_fn_pointer_type)(void *, void *, int); +typedef void *(__attribute__((alloc_size(3, 4))) * my_calloc_fn_pointer_type)(void *, void *, int, int); +extern my_malloc_fn_pointer_type malloc_function_pointer_with_typedef; +extern my_calloc_fn_pointer_type calloc_function_pointer_with_typedef; + +// CHECK-LABEL: @call_function_pointer_typedef +void call_function_pointer_typedef(void) { + malloc_function_pointer_with_typedef(NULL, NULL, 200); + // CHECK: [[INDIRECT_TYPEDEF_MALLOC_FN_PTR:%.+]] = load i8* (i8*, i8*, i32)*, i8* (i8*, i8*, i32)** @malloc_function_pointer_with_typedef, align 8 + // CHECK: call i8* [[INDIRECT_TYPEDEF_MALLOC_FN_PTR]](i8* null, i8* null, i32 200) [[INDIRECT_TYPEDEF_MALLOC_ATTR:#[0-9]+]] + calloc_function_pointer_with_typedef(NULL, NULL, 8, 4); + // CHECK: [[INDIRECT_TYPEDEF_CALLOC_FN_PTR:%.+]] = load i8* (i8*, i8*, i32, i32)*, i8* (i8*, i8*, i32, i32)** @calloc_function_pointer_with_typedef, align 8 + // CHECK: call i8* [[INDIRECT_TYPEDEF_CALLOC_FN_PTR]](i8* null, i8* null, i32 8, i32 4) [[INDIRECT_TYPEDEF_CALLOC_ATTR:#[0-9]+]] +} + +// CHECK: attributes [[DIRECT_MALLOC_ATTR]] = { allocsize(0) } +// CHECK: attributes [[DIRECT_CALLOC_ATTR]] = { allocsize(0,1) } +// CHECK: attributes [[INDIRECT_MALLOC_ATTR]] = { allocsize(1) } +// CHECK: attributes [[INDIRECT_CALLOC_ATTR]] = { allocsize(1,2) } +// CHECK: attributes [[INDIRECT_TYPEDEF_MALLOC_ATTR]] = { allocsize(2) } +// CHECK: attributes [[INDIRECT_TYPEDEF_CALLOC_ATTR]] = { allocsize(2,3) } diff --git a/clang/test/CodeGen/alloc-size.c b/clang/test/CodeGen/alloc-size.c --- a/clang/test/CodeGen/alloc-size.c +++ b/clang/test/CodeGen/alloc-size.c @@ -366,3 +366,128 @@ // CHECK: store i32 128, gi = OBJECT_SIZE_BUILTIN(alloc_uchar(128), 0); } + +void *(*malloc_function_pointer)(int)__attribute__((alloc_size(1))); +void *(*calloc_function_pointer)(int, int)__attribute__((alloc_size(1, 2))); + +// CHECK-LABEL: @test_fn_pointer +void test_fn_pointer() { + void *const vp = malloc_function_pointer(100); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 0); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 1); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 2); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 3); + + void *const arr = calloc_function_pointer(100, 5); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 0); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 1); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 2); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 3); + + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer(100), 0); + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer(100), 1); + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer(100), 2); + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer(100), 3); + + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer(100, 5), 0); + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer(100, 5), 1); + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer(100, 5), 2); + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer(100, 5), 3); + + void *const zeroPtr = malloc_function_pointer(0); + // CHECK: store i32 0 + gi = __builtin_object_size(zeroPtr, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(malloc_function_pointer(0), 0); + + void *const zeroArr1 = calloc_function_pointer(0, 1); + void *const zeroArr2 = calloc_function_pointer(1, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(zeroArr1, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(zeroArr2, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(calloc_function_pointer(1, 0), 0); + // CHECK: store i32 0 + gi = __builtin_object_size(calloc_function_pointer(0, 1), 0); +} + +typedef void *(__attribute__((warn_unused_result, alloc_size(1))) * my_malloc_function_pointer_type)(int); +typedef void *(__attribute__((alloc_size(1, 2))) * my_calloc_function_pointer_type)(int, int); +extern my_malloc_function_pointer_type malloc_function_pointer_with_typedef; +extern my_calloc_function_pointer_type calloc_function_pointer_with_typedef; + +// CHECK-LABEL: @test_fn_pointer_typedef +void test_fn_pointer_typedef() { + malloc_function_pointer_with_typedef(100); + void *const vp = malloc_function_pointer_with_typedef(100); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 0); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 1); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 2); + // CHECK: store i32 100 + gi = __builtin_object_size(vp, 3); + + void *const arr = calloc_function_pointer_with_typedef(100, 5); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 0); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 1); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 2); + // CHECK: store i32 500 + gi = __builtin_object_size(arr, 3); + + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer_with_typedef(100), 0); + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer_with_typedef(100), 1); + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer_with_typedef(100), 2); + // CHECK: store i32 100 + gi = __builtin_object_size(malloc_function_pointer_with_typedef(100), 3); + + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer_with_typedef(100, 5), 0); + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer_with_typedef(100, 5), 1); + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer_with_typedef(100, 5), 2); + // CHECK: store i32 500 + gi = __builtin_object_size(calloc_function_pointer_with_typedef(100, 5), 3); + + void *const zeroPtr = malloc_function_pointer_with_typedef(0); + // CHECK: store i32 0 + gi = __builtin_object_size(zeroPtr, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(malloc_function_pointer_with_typedef(0), 0); + + void *const zeroArr1 = calloc_function_pointer_with_typedef(0, 1); + void *const zeroArr2 = calloc_function_pointer_with_typedef(1, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(zeroArr1, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(zeroArr2, 0); + // CHECK: store i32 0 + gi = __builtin_object_size(calloc_function_pointer_with_typedef(1, 0), 0); + // CHECK: store i32 0 + gi = __builtin_object_size(calloc_function_pointer_with_typedef(0, 1), 0); +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -12,7 +12,6 @@ // CHECK-NEXT: AcquireHandle (SubjectMatchRule_function, SubjectMatchRule_type_alias, SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: Alias (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) // CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias) -// CHECK-NEXT: AllocSize (SubjectMatchRule_function) // CHECK-NEXT: AlwaysDestroy (SubjectMatchRule_variable) // CHECK-NEXT: AlwaysInline (SubjectMatchRule_function) // CHECK-NEXT: Annotate () diff --git a/clang/test/Sema/alloc-size.c b/clang/test/Sema/alloc-size.c --- a/clang/test/Sema/alloc-size.c +++ b/clang/test/Sema/alloc-size.c @@ -14,7 +14,7 @@ int fail9(int a) __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to return values that are pointers}} -int fail10 __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to functions}} +int fail10 __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to non-K&R-style functions}} void *fail11(void *a) __attribute__((alloc_size(1))); //expected-error{{'alloc_size' attribute argument may only refer to a function parameter of integer type}} @@ -22,4 +22,39 @@ void *fail12(int a) __attribute__((alloc_size(1, "abc"))); //expected-error{{'alloc_size' attribute requires parameter 2 to be an integer constant}} void *fail13(int a) __attribute__((alloc_size(1U<<31))); //expected-error{{integer constant expression evaluates to value 2147483648 that cannot be represented in a 32-bit signed integer type}} -int (*PR31453)(int) __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to functions}} +void *(*PR31453)(int)__attribute__((alloc_size(1))); + +void *KR() __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to non-K&R-style functions}} + +// Applying alloc_size to function pointers should work: +void *(__attribute__((alloc_size(1))) * func_ptr1)(int); +void *(__attribute__((alloc_size(1, 2))) func_ptr2)(int, int); + +// TODO: according to GCC documentation the following should actually be the type +// “pointer to pointer to alloc_size attributed function returning void*” and should +// therefore be supported +void *(__attribute__((alloc_size(1))) **ptr_to_func_ptr)(int); // expected-warning{{'alloc_size' attribute only applies to non-K&R-style functions}} +// The following definitions apply the attribute to the pointer to the function pointer which should not be possible +void *(*__attribute__((alloc_size(1))) * ptr_to_func_ptr2)(int); // expected-warning{{'alloc_size' attribute only applies to non-K&R-style functions}} +void *(**__attribute__((alloc_size(1))) ptr_to_func_ptr2)(int); // expected-warning{{'alloc_size' attribute only applies to non-K&R-style functions}} + +// It should also work for typedefs: +typedef void *(__attribute__((alloc_size(1))) allocator_function_typdef)(int); +typedef void *(__attribute__((alloc_size(1, 2))) * allocator_function_typdef2)(int, int); +void *(__attribute__((alloc_size(1, 2))) * allocator_function_typdef3)(int, int); +// This typedef applies the alloc_size to the pointer to the function pointer and should not be allowed +void *(**__attribute__((alloc_size(1, 2))) * allocator_function_typdef4)(int, int); // expected-warning{{'alloc_size' attribute only applies to non-K&R-style functions}} + +// We should not be warning when assigning function pointers with and without the alloc size attribute +// since it doesn't change the type of the function +typedef void *(__attribute__((alloc_size(1))) * my_malloc_fn_pointer_type)(int); +typedef void *(*my_other_malloc_fn_pointer_type)(int); +void *fn(int i); +__attribute__((alloc_size(1))) void *fn2(int i); + +int main() { + my_malloc_fn_pointer_type f = fn; + my_other_malloc_fn_pointer_type f2 = fn; + my_malloc_fn_pointer_type f3 = fn2; + my_other_malloc_fn_pointer_type f4 = fn2; +}