diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 72e0c69bf33c..2e23c6403b44 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -144,6 +144,11 @@ def AlignMac68k : InheritableAttr { let SemaHandler = 0; } +def AllocSize : Attr { + let Spellings = ["alloc_size"]; + let Args = [VariadicUnsignedArgument<"Args">]; +} + def AlwaysInline : InheritableAttr { let Spellings = ["always_inline"]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fba50ece830b..56ecbef15f69 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1483,6 +1483,8 @@ def err_attribute_argument_outof_range : Error< def err_init_priority_object_attr : Error< "can only use 'init_priority' attribute on file-scope definitions " "of objects of class type">; +def err_attribute_argument_duplicate: Error< + "'%0' attribute parameter %1 is duplicated">; def err_attribute_argument_n_not_int : Error< "'%0' attribute requires parameter %1 to be an integer constant">; def err_attribute_argument_n_not_string : Error< diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index ff25645707ad..bc1c3afbe21c 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -922,6 +922,81 @@ static void possibleTransparentUnionPointerType(QualType &T) { } } +static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + + // 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 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; + 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; + } + + uint64_t x = 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_not_int) + << "alloc_size" << Ex->getSourceRange(); + return; + } + + // check if the argument is a duplicate + SmallVectorImpl::iterator Pos; + Pos = std::find(SizeArgs.begin(), SizeArgs.end(), x); + if (Pos != SizeArgs.end()) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_duplicate) + << "alloc_size" << I.getArgNum() << Ex->getSourceRange(); + return; + } + + 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(); + 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 +3919,7 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, 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: diff --git a/clang/test/Sema/alloc_size.c b/clang/test/Sema/alloc_size.c new file mode 100644 index 000000000000..18309e3106e1 --- /dev/null +++ b/clang/test/Sema/alloc_size.c @@ -0,0 +1,22 @@ +// 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(int) __attribute__((alloc_size("xpto"))); // expected-error{{attribute requires integer constant}} + +void* fn2(void*) __attribute__((alloc_size(1))); // expected-error{{attribute requires 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}} +char fn6(unsigned) __attribute__((alloc_size(1))); // expected-warning{{only applies to functions that return a pointer}} + +void* fn7(unsigned) __attribute__((alloc_size)); // expected-error {{attribute takes at least 1 argument}} + +void *fn8(int, int) __attribute__((alloc_size(1, 1))); // expected-error {{attribute parameter 2 is duplicated}} + +void* fn9(unsigned) __attribute__((alloc_size(12345678901234567890123))); // expected-warning {{integer constant is too large for its type}} // expected-error {{attribute parameter 1 is out of bounds}}