diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index c21c19fd55c9963e1884574648eb903f32dd4ab2..2b5c3ce2daf430e02032ed7989d1e040ae5e9c76 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -455,6 +455,7 @@ public: bool hasCustomParsing() const; unsigned getMinArgs() const; unsigned getMaxArgs() const; + bool hasVariadicArg() const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; bool diagnoseLangOpts(class Sema &S) const; bool existsInTarget(const llvm::Triple &T) const; diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 476a22b628adb17376dae5a339cd30305f03e65e..34af6cf63c872aa201d77295398e43798fbbb141 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -206,3 +206,11 @@ bool AttributeList::isKnownToGCC() const { unsigned AttributeList::getSemanticSpelling() const { return getInfo(*this).SpellingIndexToSemanticSpelling(*this); } + +bool AttributeList::hasVariadicArg() const { + // If the attribute has the maximum number of optional arguments, we will + // claim that as being variadic. If we someday get an attribute that + // legitimately bumps up against that maximum, we can use another bit to track + // whether it's truly variadic or not. + return getInfo(*this).OptArgs == 15; +} diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 33577fb86f77516e093f412443c3ee6bfcd56c50..23119450f33516da4ab436b7cb2c3890b9c797e8 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -148,30 +148,43 @@ static unsigned getNumAttributeArgs(const AttributeList &Attr) { return Attr.getNumArgs() + Attr.hasParsedType(); } -/// \brief Check if the attribute has exactly as many args as Num. May -/// output an error. -static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, - unsigned Num) { - if (getNumAttributeArgs(Attr) != Num) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) - << Attr.getName() << Num; +template <typename Compare> +static bool checkAttributeNumArgsImpl(Sema &S, const AttributeList &Attr, + unsigned Num, unsigned Diag, + Compare Comp) { + if (Comp(getNumAttributeArgs(Attr), Num)) { + S.Diag(Attr.getLoc(), Diag) << Attr.getName() << Num; return false; } return true; } +/// \brief Check if the attribute has exactly as many args as Num. May +/// output an error. +static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, + unsigned Num) { + return checkAttributeNumArgsImpl(S, Attr, Num, + diag::err_attribute_wrong_number_arguments, + std::not_equal_to<unsigned>()); +} + /// \brief Check if the attribute has at least as many args as Num. May /// output an error. static bool checkAttributeAtLeastNumArgs(Sema &S, const AttributeList &Attr, unsigned Num) { - if (getNumAttributeArgs(Attr) < Num) { - S.Diag(Attr.getLoc(), diag::err_attribute_too_few_arguments) - << Attr.getName() << Num; - return false; - } + return checkAttributeNumArgsImpl(S, Attr, Num, + diag::err_attribute_too_few_arguments, + std::less<unsigned>()); +} - return true; +/// \brief Check if the attribute has at most as many args as Num. May +/// output an error. +static bool checkAttributeAtMostNumArgs(Sema &S, const AttributeList &Attr, + unsigned Num) { + return checkAttributeNumArgsImpl(S, Attr, Num, + diag::err_attribute_too_many_arguments, + std::greater<unsigned>()); } /// \brief If Expr is a valid integer constant, get the value of the integer @@ -856,8 +869,6 @@ static void handleCallableWhenAttr(Sema &S, Decl *D, static void handleParamTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!checkAttributeNumArgs(S, Attr, 1)) return; - ParamTypestateAttr::ConsumedState ParamState; if (Attr.isArgIdent(0)) { @@ -896,8 +907,6 @@ static void handleParamTypestateAttr(Sema &S, Decl *D, static void handleReturnTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!checkAttributeNumArgs(S, Attr, 1)) return; - ReturnTypestateAttr::ConsumedState ReturnState; if (Attr.isArgIdent(0)) { @@ -946,9 +955,6 @@ static void handleReturnTypestateAttr(Sema &S, Decl *D, static void handleSetTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!checkAttributeNumArgs(S, Attr, 1)) - return; - if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr)) return; @@ -974,9 +980,6 @@ static void handleSetTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) static void handleTestTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!checkAttributeNumArgs(S, Attr, 1)) - return; - if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr)) return; @@ -1593,15 +1596,8 @@ static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { } static void handleConstructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { - // check the attribute arguments. - if (Attr.getNumArgs() > 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) - << Attr.getName() << 1; - return; - } - uint32_t priority = ConstructorAttr::DefaultPriority; - if (Attr.getNumArgs() > 0 && + if (Attr.getNumArgs() && !checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), priority)) return; @@ -1611,15 +1607,8 @@ static void handleConstructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { } static void handleDestructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { - // check the attribute arguments. - if (Attr.getNumArgs() > 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) - << Attr.getName() << 1; - return; - } - uint32_t priority = DestructorAttr::DefaultPriority; - if (Attr.getNumArgs() > 0 && + if (Attr.getNumArgs() && !checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), priority)) return; @@ -1631,16 +1620,9 @@ static void handleDestructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { template <typename AttrTy> static void handleAttrWithMessage(Sema &S, Decl *D, const AttributeList &Attr) { - unsigned NumArgs = Attr.getNumArgs(); - if (NumArgs > 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) - << Attr.getName() << 1; - return; - } - // Handle the case where the attribute has a text message. StringRef Str; - if (NumArgs == 1 && !S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + if (Attr.getNumArgs() == 1 && !S.checkStringLiteralArgumentAttr(Attr, 0, Str)) return; D->addAttr(::new (S.Context) AttrTy(Attr.getRange(), S.Context, Str, @@ -2043,13 +2025,6 @@ static void handleBlocksAttr(Sema &S, Decl *D, const AttributeList &Attr) { } static void handleSentinelAttr(Sema &S, Decl *D, const AttributeList &Attr) { - // check the attribute arguments. - if (Attr.getNumArgs() > 2) { - S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) - << Attr.getName() << 2; - return; - } - unsigned sentinel = (unsigned)SentinelAttr::DefaultSentinel; if (Attr.getNumArgs() > 0) { Expr *E = Attr.getArgAsExpr(0); @@ -3249,14 +3224,6 @@ bool Sema::CheckRegparmAttr(const AttributeList &Attr, unsigned &numParams) { static void handleLaunchBoundsAttr(Sema &S, Decl *D, const AttributeList &Attr) { - // check the attribute arguments. - if (Attr.getNumArgs() != 1 && Attr.getNumArgs() != 2) { - // FIXME: 0 is not okay. - S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) - << Attr.getName() << 2; - return; - } - uint32_t MaxThreads, MinBlocks = 0; if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), MaxThreads, 1)) return; @@ -4027,11 +3994,20 @@ static bool handleCommonAttributeFeatures(Sema &S, Scope *scope, Decl *D, if (!Attr.diagnoseLangOpts(S)) return true; - // If there are no optional arguments, then checking for the argument count - // is trivial. - if (Attr.getMinArgs() == Attr.getMaxArgs() && - !checkAttributeNumArgs(S, Attr, Attr.getMinArgs())) - return true; + if (Attr.getMinArgs() == Attr.getMaxArgs()) { + // If there are no optional arguments, then checking for the argument count + // is trivial. + if (!checkAttributeNumArgs(S, Attr, Attr.getMinArgs())) + return true; + } else { + // There are optional arguments, so checking is slightly more involved. + if (Attr.getMinArgs() && + !checkAttributeAtLeastNumArgs(S, Attr, Attr.getMinArgs())) + return true; + else if (!Attr.hasVariadicArg() && Attr.getMaxArgs() && + !checkAttributeAtMostNumArgs(S, Attr, Attr.getMaxArgs())) + return true; + } // Check whether the attribute appertains to the given subject. if (!Attr.diagnoseAppertainsTo(S, D)) diff --git a/test/Sema/attr-nonnull.c b/test/Sema/attr-nonnull.c new file mode 100644 index 0000000000000000000000000000000000000000..8b8f00a20693efd2495c62042ba345bcab1f03b8 --- /dev/null +++ b/test/Sema/attr-nonnull.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +void f1(int *a1, int *a2, int *a3, int *a4, int *a5, int *a6, int *a7, + int *a8, int *a9, int *a10, int *a11, int *a12, int *a13, int *a14, + int *a15, int *a16) __attribute__((nonnull(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))); + +void f2(void) __attribute__((nonnull())); // expected-warning {{'nonnull' attribute applied to function with no pointer arguments}} diff --git a/test/SemaCUDA/launch_bounds.cu b/test/SemaCUDA/launch_bounds.cu index bed7658aed595c0337ed7bd0865be2869f23bec4..8edc41b6ce91607f950981a9b58af4acb19b3674 100644 --- a/test/SemaCUDA/launch_bounds.cu +++ b/test/SemaCUDA/launch_bounds.cu @@ -6,9 +6,6 @@ __launch_bounds__(128, 7) void Test1(void); __launch_bounds__(128) void Test2(void); __launch_bounds__(1, 2, 3) void Test3(void); // expected-error {{'launch_bounds' attribute takes no more than 2 arguments}} - -// FIXME: the error should read that the attribute takes exactly one or two arguments, but there -// is no support for such a diagnostic currently. -__launch_bounds__() void Test4(void); // expected-error {{'launch_bounds' attribute takes no more than 2 arguments}} +__launch_bounds__() void Test4(void); // expected-error {{'launch_bounds' attribute takes at least 1 argument}} int Test5 __launch_bounds__(128, 7); // expected-warning {{'launch_bounds' attribute only applies to functions and methods}} diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 1790dcbd8d3312aa31bcd7c3c7dd69bc0226fccf..0f0f89e55c6b6d0886b51cd1ac0be7fa591bba5f 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -206,6 +206,7 @@ namespace { virtual bool isEnumArg() const { return false; } virtual bool isVariadicEnumArg() const { return false; } + virtual bool isVariadic() const { return false; } virtual void writeImplicitCtorArgs(raw_ostream &OS) const { OS << getUpperName(); @@ -514,6 +515,7 @@ namespace { ArgSizeName(ArgName + "Size"), RangeName(getLowerName()) {} std::string getType() const { return Type; } + bool isVariadic() const override { return true; } void writeAccessors(raw_ostream &OS) const override { std::string IteratorType = getLowerName().str() + "_iterator"; @@ -2033,16 +2035,26 @@ void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) { } } +static bool isArgVariadic(const Record &R, StringRef AttrName) { + return createArgument(R, AttrName)->isVariadic(); +} + static void emitArgInfo(const Record &R, std::stringstream &OS) { // This function will count the number of arguments specified for the // attribute and emit the number of required arguments followed by the // number of optional arguments. std::vector<Record *> Args = R.getValueAsListOfDefs("Args"); unsigned ArgCount = 0, OptCount = 0; + bool HasVariadic = false; for (const auto *Arg : Args) { Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount; + if (!HasVariadic && isArgVariadic(*Arg, R.getName())) + HasVariadic = true; } - OS << ArgCount << ", " << OptCount; + + // If there is a variadic argument, we will set the optional argument count + // to its largest value. Since it's currently a 4-bit number, we set it to 15. + OS << ArgCount << ", " << (HasVariadic ? 15 : OptCount); } static void GenerateDefaultAppertainsTo(raw_ostream &OS) {