diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 80 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 211 |
2 files changed, 240 insertions, 51 deletions
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 1e318fe66e..2a95ecc7c7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -19,6 +19,7 @@ #include "clang/Sema/PrettyDeclStackTrace.h" #include "RAIIObjectsForParser.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringSwitch.h" using namespace clang; //===----------------------------------------------------------------------===// @@ -121,6 +122,10 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, // Availability attributes have their own grammar. if (AttrName->isStr("availability")) ParseAvailabilityAttribute(*AttrName, AttrNameLoc, attrs, endLoc); + // Thread safety attributes fit into the FIXME case above, so we + // just parse the arguments as a list of expressions + else if (IsThreadSafetyAttribute(AttrName->getName())) + ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, attrs, endLoc); // check if we have a "parameterized" attribute else if (Tok.is(tok::l_paren)) { ConsumeParen(); // ignore the left paren loc for now @@ -650,6 +655,81 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, UnavailableLoc, false, false); } +/// \brief Wrapper around a case statement checking if AttrName is +/// one of the thread safety attributes +bool Parser::IsThreadSafetyAttribute(llvm::StringRef AttrName){ + return llvm::StringSwitch<bool>(AttrName) + .Case("guarded_by", true) + .Case("guarded_var", true) + .Case("pt_guarded_by", true) + .Case("pt_guarded_var", true) + .Case("lockable", true) + .Case("scoped_lockable", true) + .Case("no_thread_safety_analysis", true) + .Case("acquired_after", true) + .Case("acquired_before", true) + .Case("exclusive_lock_function", true) + .Case("shared_lock_function", true) + .Case("exclusive_trylock_function", true) + .Case("shared_trylock_function", true) + .Case("unlock_function", true) + .Case("lock_returned", true) + .Case("locks_excluded", true) + .Case("exclusive_locks_required", true) + .Case("shared_locks_required", true) + .Default(false); +} + +/// \brief Parse the contents of thread safety attributes. These +/// should always be parsed as an expression list. +/// +/// We need to special case the parsing due to the fact that if the first token +/// of the first argument is an identifier, the main parse loop will store +/// that token as a "parameter" and the rest of +/// the arguments will be added to a list of "arguments". However, +/// subsequent tokens in the first argument are lost. We instead parse each +/// argument as an expression and add all arguments to the list of "arguments". +/// In future, we will take advantage of this special case to also +/// deal with some argument scoping issues here (for example, referring to a +/// function parameter in the attribute on that function). +void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc) { + + if (Tok.is(tok::l_paren)) { + SourceLocation LeftParenLoc = Tok.getLocation(); + ConsumeParen(); // ignore the left paren loc for now + + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + MatchRHSPunctuation(tok::r_paren, LeftParenLoc); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + // Match the ')'. + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), + ArgExprs.take(), ArgExprs.size()); + } + } else { + Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, + 0, SourceLocation(), 0, 0); + } +} + void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) { Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed) << attrs.Range; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 9fbd73f4b6..0644103b44 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -15,6 +15,7 @@ #include "TargetAttributesSema.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/Basic/SourceManager.h" @@ -27,7 +28,7 @@ using namespace sema; /// These constants match the enumerated choices of /// warn_attribute_wrong_decl_type and err_attribute_wrong_decl_type. -enum AttributeDeclType { +enum AttributeDeclKind { ExpectedFunction, ExpectedUnion, ExpectedVariableOrFunction, @@ -195,6 +196,8 @@ static inline bool isCFStringType(QualType T, ASTContext &Ctx) { return RD->getIdentifier() == &Ctx.Idents.get("__CFString"); } +/// \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 int Num) { if (Attr.getNumArgs() != Num) { @@ -205,31 +208,16 @@ static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, return true; } -/// -/// \brief Check the total number of argumenation, whether parsed by clang -/// as arguments or parameters. Outputs a warning. -/// \return false if the number of argumenation units does not match expectation -/// -static bool checkAttributeNumArgsPlusParams(Sema &S, const AttributeList &Attr, - unsigned int Num, - bool moreok = false) { - unsigned int numArgsPlusParams = 0; - - if (Attr.getParameterName()) - numArgsPlusParams++; - numArgsPlusParams += Attr.getNumArgs(); - - if (moreok && numArgsPlusParams < Num) { +/// \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 int Num) { + if (Attr.getNumArgs() < Num) { S.Diag(Attr.getLoc(), diag::err_attribute_too_few_arguments) << Num; return false; } - if (!moreok && numArgsPlusParams != Num) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << Num; - return false; - } - return true; } @@ -246,6 +234,12 @@ static bool mayBeSharedVariable(const Decl *D) { return false; } +/// \brief Check if the passed-in expression is of type int or bool. +static bool isIntOrBool(Expr *Exp) { + QualType QT = Exp->getType(); + return QT->isBooleanType() || QT->isIntegerType(); +} + /// /// \brief Check if passed in Decl is a pointer type. /// Note that this function may produce an error message. @@ -265,6 +259,71 @@ static bool checkIsPointer(Sema &S, const Decl *D, const AttributeList &Attr) { return false; } +/// \brief Checks that the passed in QualType either is of RecordType or points +/// to RecordType. Returns the relevant RecordType, null if it does not exit. +const RecordType *getRecordType(QualType QT) { + const RecordType *RT = QT->getAs<RecordType>(); + // now check if we point to record type + if(!RT && QT->isPointerType()){ + QualType PT = QT->getAs<PointerType>()->getPointeeType(); + RT = PT->getAs<RecordType>(); + } + return RT; +} + +/// \brief Thread Safety Analysis: Checks that all attribute arguments, starting +/// from Sidx, resolve to a lockable object. May flag an error. +static bool checkAttrArgsAreLockableObjs(Sema & S, Decl *D, + const AttributeList & Attr, + int Sidx = 0, + bool ParamIdxOk = false) { + for(unsigned int Idx = Sidx; Idx < Attr.getNumArgs(); ++Idx) { + Expr *ArgExp = Attr.getArg(Idx); + if (ArgExp->isTypeDependent()) + continue; + + QualType Arg_QT = ArgExp->getType(); + + // Get record type. + // first see if we can just cast to record type, or point to record type + const RecordType *RT = getRecordType(Arg_QT); + + // now check if we idx into a record type function param + if (!RT && ParamIdxOk) { + FunctionDecl *FD = dyn_cast <FunctionDecl>(D); + IntegerLiteral *IL = dyn_cast<IntegerLiteral>(ArgExp); + if(FD && IL) { + unsigned int NumParams = FD->getNumParams(); + llvm::APInt ArgValue = IL->getValue(); + uint64_t ParamIdx_from1 = ArgValue.getZExtValue(); + uint64_t ParamIdx_from0 = ParamIdx_from1 - 1; + if(!ArgValue.isStrictlyPositive() || ParamIdx_from1 > NumParams) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_range) + << Attr.getName() << Idx + 1 << NumParams; + return false; + } + Arg_QT = FD->getParamDecl(ParamIdx_from0)->getType(); + RT = getRecordType(Arg_QT); + } + } + + // Flag error if could not get record type for this argument + if (!RT) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_class) + << Attr.getName(); + return false; + } + + // Flag error if the type is not lockable + if (!RT->getDecl()->getAttr<LockableAttr>()) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_lockable) + << Attr.getName(); + return false; + } + } + return true; +} + //===----------------------------------------------------------------------===// // Attribute Implementations //===----------------------------------------------------------------------===// @@ -283,7 +342,7 @@ static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr, // D must be either a member field or global (potentially shared) variable. if (!mayBeSharedVariable(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << 15; /*fields and global vars*/; + << Attr.getName() << ExpectedFieldOrGlobalVar; return; } @@ -297,22 +356,26 @@ static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr, } static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool pointer = false) { + bool pointer = false) { assert(!Attr.isInvalid()); - if (!checkAttributeNumArgsPlusParams(S, Attr, 1)) + if (!checkAttributeNumArgs(S, Attr, 1)) return; // D must be either a member field or global (potentially shared) variable. if (!mayBeSharedVariable(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << 15; /*fields and global vars*/; + << Attr.getName() << ExpectedFieldOrGlobalVar; return; } if (pointer && !checkIsPointer(S, D, Attr)) return; + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr)) + return; + if (pointer) D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getLoc(), S.Context)); else @@ -346,7 +409,7 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D, if (!checkAttributeNumArgs(S, Attr, 0)) return; - if (!isFunction(D)) { + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; @@ -360,16 +423,32 @@ static void handleAcquireOrderAttr(Sema &S, Decl *D, const AttributeList &Attr, bool before) { assert(!Attr.isInvalid()); - if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return; // D must be either a member field or global (potentially shared) variable. - if (!mayBeSharedVariable(D)) { + ValueDecl *VD = dyn_cast<ValueDecl>(D); + if (!VD || !mayBeSharedVariable(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << 15; /*fields and global vars*/; + << Attr.getName() << ExpectedFieldOrGlobalVar; return; } + // Check that this attribute only applies to lockable types + QualType QT = VD->getType(); + if (!QT->isDependentType()) { + const RecordType *RT = getRecordType(QT); + if (!RT || !RT->getDecl()->getAttr<LockableAttr>()) { + S.Diag(Attr.getLoc(), diag::err_attribute_decl_not_lockable) + << Attr.getName(); + return; + } + } + + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr)) + return; + if (before) D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getLoc(), S.Context)); else @@ -377,17 +456,22 @@ static void handleAcquireOrderAttr(Sema &S, Decl *D, const AttributeList &Attr, } static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool exclusive = false) { + bool exclusive = false) { assert(!Attr.isInvalid()); // zero or more arguments ok - if (!isFunction(D)) { + // check that the attribute is applied to a function + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr, 0, /*ParamIdxOk=*/true)) + return; + if (exclusive) D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getLoc(), S.Context)); @@ -397,93 +481,118 @@ static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, } static void handleTrylockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool exclusive = false) { + bool exclusive = false) { assert(!Attr.isInvalid()); - if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return; - if (!isFunction(D)) { + + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } + if (!isIntOrBool(Attr.getArg(0))) { + S.Diag(Attr.getLoc(), diag::err_attribute_first_argument_not_int_or_bool) + << Attr.getName(); + return; + } + + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr, 1)) + return; + if (exclusive) D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getLoc(), - S.Context)); + S.Context)); else D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getLoc(), - S.Context)); - + S.Context)); } static void handleLocksRequiredAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool exclusive = false) { + bool exclusive = false) { assert(!Attr.isInvalid()); - if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return; - if (!isFunction(D)) { + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr)) + return; + if (exclusive) D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getLoc(), - S.Context)); + S.Context)); else D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getLoc(), - S.Context)); + S.Context)); } - static void handleUnlockFunAttr(Sema &S, Decl *D, - const AttributeList &Attr) { + const AttributeList &Attr) { assert(!Attr.isInvalid()); // zero or more arguments ok - if (!isFunction(D)) { + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr, 0, /*ParamIdxOk=*/true)) + return; + D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getLoc(), S.Context)); } static void handleLockReturnedAttr(Sema &S, Decl *D, - const AttributeList &Attr) { + const AttributeList &Attr) { assert(!Attr.isInvalid()); - if (!checkAttributeNumArgsPlusParams(S, Attr, 1)) + if (!checkAttributeNumArgs(S, Attr, 1)) return; - if (!isFunction(D)) { + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr)) + return; + D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getLoc(), S.Context)); } static void handleLocksExcludedAttr(Sema &S, Decl *D, - const AttributeList &Attr) { + const AttributeList &Attr) { assert(!Attr.isInvalid()); - if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return; - if (!isFunction(D)) { + if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } + // check that all arguments are lockable objects + if (!checkAttrArgsAreLockableObjs(S, D, Attr)) + return; + D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getLoc(), S.Context)); } |