Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1070,7 +1070,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; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -129,8 +129,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. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -6264,6 +6264,16 @@ llvm_unreachable("Unknown type of decl!"); } +template +static void copyAttrFromTypedefToDecl(Sema &S, Decl *D, const TypedefType *TT) { + TypedefNameDecl *TND = TT->getDecl(); + if (AttrTy *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, @@ -6706,6 +6716,15 @@ // 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) + if (R->isFunctionPointerType()) { + if (auto TT = R->getAs()) { + copyAttrFromTypedefToDecl(*this, NewVD, TT); + } + } + if (getLangOpts().CUDA || getLangOpts().OpenMPIsDevice) { if (EmitTLSUnsupportedError && ((getLangOpts().CUDA && DeclAttrsMatchCUDAMode(getLangOpts(), NewVD)) || Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -810,20 +810,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; @@ -834,8 +834,10 @@ !checkAttributeAtMostNumArgs(S, AL, 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; } @@ -845,7 +847,7 @@ // Parameter indices are 1-indexed, hence Index=1 if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNoVal, /*Index=*/1)) return; - if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/0)) + if (!checkParamIsIntegerType(S, D, AL, /*AttrArgNo=*/0)) return; ParamIdx SizeArgNo(SizeArgNoVal, D); @@ -856,7 +858,7 @@ // Parameter indices are 1-based, hence Index=2 if (!checkPositiveIntArgument(S, AL, NumberExpr, Val, /*Index=*/2)) return; - if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/1)) + if (!checkParamIsIntegerType(S, D, AL, /*AttrArgNo=*/1)) return; NumberArgNo = ParamIdx(Val, D); } Index: test/CodeGen/alloc-size-fnptr.c =================================================================== --- /dev/null +++ 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) } Index: test/CodeGen/alloc-size.c =================================================================== --- test/CodeGen/alloc-size.c +++ test/CodeGen/alloc-size.c @@ -1,3 +1,4 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - // RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck %s #define NULL ((void *)0) @@ -350,3 +351,128 @@ // CHECK: store i32 -1 gi = __builtin_object_size(my_signed_calloc(-2, 1), 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); +} Index: test/Sema/alloc-size.c =================================================================== --- test/Sema/alloc-size.c +++ 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,25 @@ 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}}