[cfe-dev] __attribute__((alloc_size(foo))
Nuno Lopes
nunoplopes at sapo.pt
Wed May 23 10:54:54 PDT 2012
Hi,
Please find in attach a patch that implements the attribute
alloc_size. This attribute was introduced with GCC 4.3.
Intended usage:
void* my_malloc(size_t) __attribute__((alloc_size(1)));
void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)));
void* my_realloc(void*, size_t) __attribute__((alloc_size(2)));
The attribute basically declares that a function returns memory of
size given by the product of the parameters listed in the attribute.
More info: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
This patch only adds Sema support for the attribute. Wiring for
codegen and static analyzer will come later.
Any comments, suggestions, etc?
Thanks,
Nuno
P.S.: This attribute is not expressive enough for functions that e.g.
return x elements, like 'alloc_foo_objects(int howmany)'. However,
since gcc already has this attribute and software out there is already
using it, I think we should support it, even if we come up with a
better attribute later.
-------------- next part --------------
Index: test/Sema/alloc_size.c
===================================================================
--- test/Sema/alloc_size.c (revision 0)
+++ test/Sema/alloc_size.c (revision 0)
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+void* my_malloc(unsigned char) __attribute__((alloc_size(1)));
+void* my_calloc(unsigned char, short) __attribute__((alloc_size(1,2)));
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(2)));
+
+
+void* fn1(void*) __attribute__((alloc_size(1))); // expected-error{{requires parameter 1 to be an integer constant}}
+void* fn2(void*,char*) __attribute__((alloc_size(1,2))); // expected-error{{requires parameter 1 to be an integer constant}} // expected-error{{requires parameter 2 to be an integer constant}}
+
+void* fn3(unsigned) __attribute__((alloc_size(0))); // expected-error{{attribute parameter 1 is out of bounds}}
+void* fn4(unsigned) __attribute__((alloc_size(2))); // expected-error{{attribute parameter 1 is out of bounds}}
+
+void fn5(unsigned) __attribute__((alloc_size(1))); // expected-warning{{only applies to functions that return a pointer}}
+void fn6(unsigned) __attribute__((alloc_size(1))); // expected-warning{{only applies to functions that return a pointer}}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td (revision 157329)
+++ include/clang/Basic/Attr.td (working copy)
@@ -144,6 +144,11 @@
let SemaHandler = 0;
}
+def AllocSize : Attr {
+ let Spellings = ["alloc_size"];
+ let Args = [VariadicUnsignedArgument<"Args">];
+}
+
def AlwaysInline : InheritableAttr {
let Spellings = ["always_inline"];
}
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp (revision 157329)
+++ lib/Sema/SemaDeclAttr.cpp (working copy)
@@ -922,6 +922,71 @@
}
}
+static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // In C++ the implicit 'this' function parameter also counts, and they are
+ // counted from one.
+ bool HasImplicitThisParam = isInstanceMethod(D);
+ unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam;
+
+ SmallVector<unsigned, 8> SizeArgs;
+
+ for (AttributeList::arg_iterator I = Attr.arg_begin(),
+ E = Attr.arg_end(); I!=E; ++I) {
+ // The argument must be an integer constant expression.
+ Expr *Ex = *I;
+ llvm::APSInt ArgNum(32);
+ if (Ex->isTypeDependent() || Ex->isValueDependent() ||
+ !Ex->isIntegerConstantExpr(ArgNum, S.Context)) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int)
+ << "alloc_size" << Ex->getSourceRange();
+ return;
+ }
+
+ unsigned x = (unsigned) ArgNum.getZExtValue();
+
+ if (x < 1 || x > NumArgs) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds)
+ << "alloc_size" << I.getArgNum() << Ex->getSourceRange();
+ return;
+ }
+
+ --x;
+ if (HasImplicitThisParam) {
+ if (x == 0) {
+ S.Diag(Attr.getLoc(),
+ diag::err_attribute_invalid_implicit_this_argument)
+ << "alloc_size" << Ex->getSourceRange();
+ return;
+ }
+ --x;
+ }
+
+ // check if the function argument is of an integer type
+ QualType T = getFunctionOrMethodArgType(D, x).getNonReferenceType();
+ if (!T->isIntegerType()) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_int)
+ << "alloc_size" << x+1 << Ex->getSourceRange();
+ continue;
+ }
+
+ SizeArgs.push_back(x);
+ }
+
+ // check if the function returns a pointer
+ if (!getFunctionType(D)->getResultType()->isAnyPointerType()) {
+ S.Diag(Attr.getLoc(), diag::warn_ns_attribute_wrong_return_type)
+ << "alloc_size" << 0 /*function*/<< 1 /*pointer*/ << D->getSourceRange();
+ }
+
+ unsigned size = SizeArgs.size();
+ if (size == 0)
+ return;
+ unsigned* start = &SizeArgs[0];
+ llvm::array_pod_sort(start, start + size);
+ D->addAttr(::new (S.Context) AllocSizeAttr(Attr.getRange(), S.Context, start,
+ size));
+}
+
static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) {
// GCC ignores the nonnull attribute on K&R style function prototypes, so we
// ignore it as well
@@ -3844,6 +3909,7 @@
break;
case AttributeList::AT_alias: handleAliasAttr (S, D, Attr); break;
case AttributeList::AT_aligned: handleAlignedAttr (S, D, Attr); break;
+ case AttributeList::AT_alloc_size: handleAllocSizeAttr (S, D, Attr); break;
case AttributeList::AT_always_inline:
handleAlwaysInlineAttr (S, D, Attr); break;
case AttributeList::AT_analyzer_noreturn:
More information about the cfe-dev
mailing list