From: Aaron Ballman Date: Thu, 16 Jan 2014 13:55:42 +0000 (+0000) Subject: Factored some function-like type reasoning out of SemaDeclAttr and onto Decl itself... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0e00e32712f8d1cc444cb17172a828d4ea573c72;p=clang Factored some function-like type reasoning out of SemaDeclAttr and onto Decl itself. This allows for more declarative subjects in attribute tablegen where the attribute appertains to something function-like, but not strictly a FunctionDecl. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@199387 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 4d5ff662c7..fde90b2d5a 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -32,6 +32,7 @@ class DeclarationName; class DependentDiagnostic; class EnumDecl; class FunctionDecl; +class FunctionType; class LinkageComputer; class LinkageSpecDecl; class Module; @@ -943,6 +944,11 @@ public: void dumpColor() const; void dump(raw_ostream &Out) const; + /// \brief Looks through the Decl's underlying type to extract a FunctionType + /// when possible. Will return null if the type underlying the Decl does not + /// have a FunctionType. + const FunctionType *getFunctionType(bool BlocksToo = true) const; + private: void setAttrsImpl(const AttrVec& Attrs, ASTContext &Ctx); void setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 6d60c47c75..78fdedc20f 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -56,6 +56,21 @@ def SharedVar : SubsetSubjecthasGlobalStorage()}]>; +// FIXME: this hack is needed because DeclNodes.td defines the base Decl node +// type to be a class, not a definition. This makes it impossible to create an +// attribute subject which accepts a Decl. Normally, this is not a problem, +// because the attribute can have no Subjects clause to accomplish this. But in +// the case of a SubsetSubject, there's no way to express it without this hack. +def DeclBase : AttrSubject; +def FunctionLike : SubsetSubjectgetFunctionType(false) != NULL}]>; + +def HasFunctionProto : SubsetSubjectgetFunctionType(true) != NULL && + isa(S->getFunctionType())) || + isa(S) || + isa(S)}]>; + // A single argument to an attribute class Argument { string Name = name; @@ -426,6 +441,8 @@ def CUDALaunchBounds : InheritableAttr { let Spellings = [GNU<"launch_bounds">]; let Args = [IntArgument<"MaxThreads">, DefaultIntArgument<"MinBlocks", 0>]; let LangOpts = [CUDA]; + let Subjects = SubjectList<[ObjCMethod, FunctionLike], WarnDiag, + "ExpectedFunctionOrMethod">; // An AST node is created for this attribute, but is not used by other parts // of the compiler. However, this node needs to exist in the AST because // non-LLVM backends may be relying on the attribute's presence. @@ -536,11 +553,15 @@ def Format : InheritableAttr { let Spellings = [GNU<"format">, CXX11<"gnu", "format">]; let Args = [IdentifierArgument<"Type">, IntArgument<"FormatIdx">, IntArgument<"FirstArg">]; + let Subjects = SubjectList<[ObjCMethod, Block, FunctionLike, + HasFunctionProto], WarnDiag, "ExpectedFunction">; } def FormatArg : InheritableAttr { let Spellings = [GNU<"format_arg">, CXX11<"gnu", "format_arg">]; let Args = [IntArgument<"FormatIdx">]; + let Subjects = SubjectList<[ObjCMethod, FunctionLike, HasFunctionProto], + WarnDiag, "ExpectedFunction">; } def GNUInline : InheritableAttr { @@ -656,6 +677,8 @@ def NoMips16 : InheritableAttr, TargetSpecificAttr { def NonNull : InheritableAttr { let Spellings = [GNU<"nonnull">, CXX11<"gnu", "nonnull">]; + let Subjects = SubjectList<[ObjCMethod, FunctionLike, HasFunctionProto], + WarnDiag, "ExpectedFunction">; let Args = [VariadicUnsignedArgument<"Args">]; let AdditionalMembers = [{bool isNonNull(unsigned idx) const { @@ -804,6 +827,8 @@ def Ownership : InheritableAttr { } }]; let Args = [IdentifierArgument<"Module">, VariadicUnsignedArgument<"Args">]; + let Subjects = SubjectList<[FunctionLike, HasFunctionProto], WarnDiag, + "ExpectedFunction">; } def Packed : InheritableAttr { @@ -930,6 +955,9 @@ def ObjCRequiresPropertyDefs : InheritableAttr { def Unused : InheritableAttr { let Spellings = [GNU<"unused">, CXX11<"gnu", "unused">]; + let Subjects = SubjectList<[Var, ObjCIvar, Type, Label, Field, ObjCMethod, + FunctionLike], WarnDiag, + "ExpectedVariableFunctionOrLabel">; } def Used : InheritableAttr { @@ -985,6 +1013,8 @@ def WarnUnusedResult : InheritableAttr { let Spellings = [GNU<"warn_unused_result">, CXX11<"clang", "warn_unused_result">, CXX11<"gnu", "warn_unused_result">]; + let Subjects = SubjectList<[ObjCMethod, CXXRecord, FunctionLike], WarnDiag, + "ExpectedFunctionMethodOrClass">; } def Weak : InheritableAttr { diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 8e7373631d..46adad6c3c 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -701,6 +701,24 @@ bool Decl::AccessDeclContextSanity() const { static Decl::Kind getKind(const Decl *D) { return D->getKind(); } static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); } +const FunctionType *Decl::getFunctionType(bool BlocksToo) const { + QualType Ty; + if (const ValueDecl *D = dyn_cast(this)) + Ty = D->getType(); + else if (const TypedefNameDecl *D = dyn_cast(this)) + Ty = D->getUnderlyingType(); + else + return 0; + + if (Ty->isFunctionPointerType()) + Ty = Ty->getAs()->getPointeeType(); + else if (BlocksToo && Ty->isBlockPointerType()) + Ty = Ty->getAs()->getPointeeType(); + + return Ty->getAs(); +} + + /// Starting at a given context (a Decl or DeclContext), look for a /// code context that is not a closure (a lambda, block, etc.). template static Decl *getNonClosureContext(T *D) { diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 49ab6ed1b8..b8116a7e8a 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -43,52 +43,11 @@ namespace AttributeLangSupport { // Helper functions //===----------------------------------------------------------------------===// -static const FunctionType *getFunctionType(const Decl *D, - bool blocksToo = true) { - QualType Ty; - if (const ValueDecl *decl = dyn_cast(D)) - Ty = decl->getType(); - else if (const TypedefNameDecl* decl = dyn_cast(D)) - Ty = decl->getUnderlyingType(); - else - return 0; - - if (Ty->isFunctionPointerType()) - Ty = Ty->getAs()->getPointeeType(); - else if (blocksToo && Ty->isBlockPointerType()) - Ty = Ty->getAs()->getPointeeType(); - - return Ty->getAs(); -} - -// FIXME: We should provide an abstraction around a method or function -// to provide the following bits of information. - -/// isFunction - Return true if the given decl has function -/// type (function or function-typed variable). -static bool isFunction(const Decl *D) { - return getFunctionType(D, false) != NULL; -} - /// isFunctionOrMethod - Return true if the given decl has function /// type (function or function-typed variable) or an Objective-C /// method. static bool isFunctionOrMethod(const Decl *D) { - return isFunction(D) || isa(D); -} - -/// isFunctionOrMethodOrBlock - Return true if the given decl has function -/// type (function or function-typed variable) or an Objective-C -/// method or a block. -static bool isFunctionOrMethodOrBlock(const Decl *D) { - if (isFunctionOrMethod(D)) - return true; - // check for block is more involved. - if (const VarDecl *V = dyn_cast(D)) { - QualType Ty = V->getType(); - return Ty->isBlockPointerType(); - } - return isa(D); + return (D->getFunctionType() != NULL) || isa(D); } /// Return true if the given decl has a declarator that should have @@ -103,19 +62,16 @@ static bool hasDeclarator(const Decl *D) { /// information. This decl should have already passed /// isFunctionOrMethod or isFunctionOrMethodOrBlock. static bool hasFunctionProto(const Decl *D) { - if (const FunctionType *FnTy = getFunctionType(D)) + if (const FunctionType *FnTy = D->getFunctionType()) return isa(FnTy); - else { - assert(isa(D) || isa(D)); - return true; - } + return isa(D) || isa(D); } /// getFunctionOrMethodNumArgs - Return number of function or method /// arguments. It is an error to call this on a K&R function (use /// hasFunctionProto first). static unsigned getFunctionOrMethodNumArgs(const Decl *D) { - if (const FunctionType *FnTy = getFunctionType(D)) + if (const FunctionType *FnTy = D->getFunctionType()) return cast(FnTy)->getNumArgs(); if (const BlockDecl *BD = dyn_cast(D)) return BD->getNumParams(); @@ -123,7 +79,7 @@ static unsigned getFunctionOrMethodNumArgs(const Decl *D) { } static QualType getFunctionOrMethodArgType(const Decl *D, unsigned Idx) { - if (const FunctionType *FnTy = getFunctionType(D)) + if (const FunctionType *FnTy = D->getFunctionType()) return cast(FnTy)->getArgType(Idx); if (const BlockDecl *BD = dyn_cast(D)) return BD->getParamDecl(Idx)->getType(); @@ -132,13 +88,13 @@ static QualType getFunctionOrMethodArgType(const Decl *D, unsigned Idx) { } static QualType getFunctionOrMethodResultType(const Decl *D) { - if (const FunctionType *FnTy = getFunctionType(D)) + if (const FunctionType *FnTy = D->getFunctionType()) return cast(FnTy)->getResultType(); return cast(D)->getResultType(); } static bool isFunctionOrMethodVariadic(const Decl *D) { - if (const FunctionType *FnTy = getFunctionType(D)) { + if (const FunctionType *FnTy = D->getFunctionType()) { const FunctionProtoType *proto = cast(FnTy); return proto->isVariadic(); } else if (const BlockDecl *BD = dyn_cast(D)) @@ -1201,14 +1157,6 @@ static void possibleTransparentUnionPointerType(QualType &T) { } 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 - if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunction; - return; - } - SmallVector NonNullArgs; for (unsigned i = 0; i < Attr.getNumArgs(); ++i) { Expr *Ex = Attr.getArgAsExpr(i); @@ -1306,12 +1254,6 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) { break; } - if (!isFunction(D) || !hasFunctionProto(D)) { - S.Diag(AL.getLoc(), diag::warn_attribute_wrong_decl_type) - << AL.getName() << ExpectedFunction; - return; - } - IdentifierInfo *Module = AL.getArgAsIdent(0)->Ident; // Normalize the argument, __foo__ becomes foo. @@ -1636,19 +1578,6 @@ static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D, Attr.getAttributeSpellingListIndex())); } -static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!isa(D) && !isa(D) && !isFunctionOrMethod(D) && - !isa(D) && !isa(D) && !isa(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedVariableFunctionOrLabel; - return; - } - - D->addAttr(::new (S.Context) - UnusedAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); -} - static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (const VarDecl *VD = dyn_cast(D)) { if (VD->hasLocalStorage()) { @@ -2185,7 +2114,8 @@ static void handleSentinelAttr(Sema &S, Decl *D, const AttributeList &Attr) { } else if (const VarDecl *V = dyn_cast(D)) { QualType Ty = V->getType(); if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) { - const FunctionType *FT = Ty->isFunctionPointerType() ? getFunctionType(D) + const FunctionType *FT = Ty->isFunctionPointerType() + ? D->getFunctionType() : Ty->getAs()->getPointeeType()->getAs(); if (!cast(FT)->isVariadic()) { int m = Ty->isFunctionPointerType() ? 0 : 1; @@ -2208,13 +2138,7 @@ static void handleSentinelAttr(Sema &S, Decl *D, const AttributeList &Attr) { } static void handleWarnUnusedResult(Sema &S, Decl *D, const AttributeList &Attr) { - if (!isFunction(D) && !isa(D) && !isa(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionMethodOrClass; - return; - } - - if (isFunction(D) && getFunctionType(D)->getResultType()->isVoidType()) { + if (D->getFunctionType() && D->getFunctionType()->getResultType()->isVoidType()) { S.Diag(Attr.getLoc(), diag::warn_attribute_void_function_method) << Attr.getName() << 0; return; @@ -2408,12 +2332,6 @@ static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) { /// Handle __attribute__((format_arg((idx)))) attribute based on /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunction; - return; - } - Expr *IdxExpr = Attr.getArgAsExpr(0); uint64_t ArgIdx; if (!checkFunctionOrMethodArgumentIndex(S, D, Attr, 1, IdxExpr, ArgIdx)) @@ -2558,12 +2476,6 @@ static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) { return; } - if (!isFunctionOrMethodOrBlock(D) || !hasFunctionProto(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunction; - return; - } - // In C++ the implicit 'this' function parameter also counts, and they are // counted from one. bool HasImplicitThisParam = isInstanceMethod(D); @@ -3295,12 +3207,6 @@ static void handleLaunchBoundsAttr(Sema &S, Decl *D, return; } - if (!isFunctionOrMethod(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; - return; - } - uint32_t MaxThreads, MinBlocks = 0; if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), MaxThreads, 1)) return; @@ -4120,7 +4026,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, break; case AttributeList::AT_ObjCRequiresPropertyDefs: handleSimpleAttribute(S, D, Attr); break; - case AttributeList::AT_Unused: handleUnusedAttr (S, D, Attr); break; + case AttributeList::AT_Unused: + handleSimpleAttribute(S, D, Attr); break; case AttributeList::AT_ReturnsTwice: handleSimpleAttribute(S, D, Attr); break; case AttributeList::AT_Used: handleUsedAttr (S, D, Attr); break; diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 47de7da6a5..672589c0b4 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -2061,6 +2061,12 @@ static std::string CalculateDiagnostic(const Record &S) { return ""; } +static std::string GetSubjectWithSuffix(const Record *R) { + std::string B = R->getName(); + if (B == "DeclBase") + return "Decl"; + return B + "Decl"; +} static std::string GenerateCustomAppertainsTo(const Record &Subject, raw_ostream &OS) { std::string FnName = "is" + Subject.getName(); @@ -2082,9 +2088,9 @@ static std::string GenerateCustomAppertainsTo(const Record &Subject, } OS << "static bool " << FnName << "(const Decl *D) {\n"; - OS << " const " << Base->getName() << "Decl *S = dyn_cast<"; - OS << Base->getName(); - OS << "Decl>(D);\n"; + OS << " const " << GetSubjectWithSuffix(Base) << " *S = dyn_cast<"; + OS << GetSubjectWithSuffix(Base); + OS << ">(D);\n"; OS << " return S && " << Subject.getValueAsString("CheckCode") << ";\n"; OS << "}\n\n"; @@ -2126,7 +2132,7 @@ static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { if ((*I)->isSubClassOf("SubsetSubject")) { SS << "!" << GenerateCustomAppertainsTo(**I, OS) << "(D)"; } else { - SS << "!isa<" << (*I)->getName() << "Decl>(D)"; + SS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; } if (I + 1 != E)