This revision was automatically updated to reflect the committed changes.
Closed by commit rGdc4abca7662b: Handle alloc_size attribute on function 
pointers (authored by arichardson).
Herald added a project: clang.

Changed prior to commit:
  https://reviews.llvm.org/D55212?vs=177519&id=336519#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D55212/new/

https://reviews.llvm.org/D55212

Files:
  clang/include/clang/Basic/Attr.td
  clang/lib/AST/ExprConstant.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/alloc-size-fnptr.c
  clang/test/CodeGen/alloc-size.c
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/alloc-size.c

Index: clang/test/Sema/alloc-size.c
===================================================================
--- clang/test/Sema/alloc-size.c
+++ 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;
+}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ 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 ()
Index: clang/test/CodeGen/alloc-size.c
===================================================================
--- clang/test/CodeGen/alloc-size.c
+++ 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);
+}
Index: clang/test/CodeGen/alloc-size-fnptr.c
===================================================================
--- /dev/null
+++ 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) }
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -788,20 +788,20 @@
 ///
 /// AttrArgNo is used to actually retrieve the argument, so it's base-0.
 template <typename AttrInfo>
-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<FunctionDecl>(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);
   }
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -6831,6 +6831,16 @@
   return true;
 }
 
+template <typename AttrTy>
+static void copyAttrFromTypedefToDecl(Sema &S, Decl *D, const TypedefType *TT) {
+  const TypedefNameDecl *TND = TT->getDecl();
+  if (const auto *Attribute = TND->getAttr<AttrTy>()) {
+    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<TypedefType>())
+      copyAttrFromTypedefToDecl<AllocSizeAttr>(*this, NewVD, TT);
+
   if (getLangOpts().CUDA || getLangOpts().OpenMPIsDevice ||
       getLangOpts().SYCLIsDevice) {
     if (EmitTLSUnsupportedError &&
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ 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<AllocSizeAttr>() : nullptr;
+    if (const FunctionDecl *DirectCallee = CE->getDirectCallee())
+      return DirectCallee->getAttr<AllocSizeAttr>();
+    if (const Decl *IndirectCallee = CE->getCalleeDecl())
+      return IndirectCallee->getAttr<AllocSizeAttr>();
+    return nullptr;
   }
 
   /// Attempts to unwrap a CallExpr (with an alloc_size attribute) from an Expr.
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ 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;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to