Index: clang/include/clang/AST/EvaluatedExprVisitor.h =================================================================== --- clang/include/clang/AST/EvaluatedExprVisitor.h +++ clang/include/clang/AST/EvaluatedExprVisitor.h @@ -43,6 +43,7 @@ // other sub-expressions). void VisitDeclRefExpr(PTR(DeclRefExpr) E) { } void VisitOffsetOfExpr(PTR(OffsetOfExpr) E) { } + void VisitBuiltinInstanceMemberExpr(PTR(BuiltinInstanceMemberExpr) E) {} void VisitUnaryExprOrTypeTraitExpr(PTR(UnaryExprOrTypeTraitExpr) E) { } void VisitExpressionTraitExpr(PTR(ExpressionTraitExpr) E) { } void VisitBlockExpr(PTR(BlockExpr) E) { } Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -2567,6 +2567,32 @@ friend TrailingObjects; }; +class BuiltinInstanceMemberExpr final : public Expr { +public: + const FieldDecl *F; + BuiltinInstanceMemberExpr(const ASTContext &C, const FieldDecl *F); + + static BuiltinInstanceMemberExpr *Create(const ASTContext &C, + const FieldDecl *F); + + SourceLocation getBeginLoc() const LLVM_READONLY { return SourceLocation(); } + SourceLocation getEndLoc() const LLVM_READONLY { return SourceLocation(); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BuiltinInstanceMemberExprClass; + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + const FieldDecl *getFieldDecl() const { return F; } +}; + /// UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) /// expression operand. Used for sizeof/alignof (C99 6.5.3.4) and /// vec_step (OpenCL 1.1 6.11.12). Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -2600,6 +2600,8 @@ TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(BuiltinInstanceMemberExpr, {}) + DEF_TRAVERSE_STMT(UnaryExprOrTypeTraitExpr, { // The child-iterator will pick up the arg if it's an expression, // but not if it's a type. Index: clang/include/clang/Basic/StmtNodes.td =================================================================== --- clang/include/clang/Basic/StmtNodes.td +++ clang/include/clang/Basic/StmtNodes.td @@ -99,6 +99,7 @@ def GenericSelectionExpr : StmtNode; def PseudoObjectExpr : StmtNode; def SourceLocExpr : StmtNode; +def BuiltinInstanceMemberExpr : StmtNode; // Wrapper expressions def FullExpr : StmtNode; Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -435,6 +435,7 @@ KEYWORD(__attribute , KEYALL) KEYWORD(__builtin_choose_expr , KEYALL) KEYWORD(__builtin_offsetof , KEYALL) +KEYWORD(__builtin_instance_member , KEYALL) KEYWORD(__builtin_FILE , KEYALL) KEYWORD(__builtin_FILE_NAME , KEYALL) KEYWORD(__builtin_FUNCTION , KEYALL) Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6012,6 +6012,7 @@ ParsedType ParsedArgTy, ArrayRef Components, SourceLocation RParenLoc); + ExprResult ActOnBuiltinInstanceMember(const IdentifierInfo *II); // __builtin_choose_expr(constExpr, expr1, expr2) ExprResult ActOnChooseExpr(SourceLocation BuiltinLoc, Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -1637,6 +1637,19 @@ return end; } +BuiltinInstanceMemberExpr * +BuiltinInstanceMemberExpr::Create(const ASTContext &C, const FieldDecl *F) { + void *Mem = C.Allocate(sizeof(BuiltinInstanceMemberExpr)); + return new (Mem) BuiltinInstanceMemberExpr(C, F); +} + +BuiltinInstanceMemberExpr::BuiltinInstanceMemberExpr(const ASTContext &C, + const FieldDecl *F) + : Expr(BuiltinInstanceMemberExprClass, F->getType(), VK_PRValue, + OK_Ordinary) { + this->F = F; +} + OffsetOfExpr *OffsetOfExpr::Create(const ASTContext &C, QualType type, SourceLocation OperatorLoc, TypeSourceInfo *tsi, Index: clang/lib/AST/StmtPrinter.cpp =================================================================== --- clang/lib/AST/StmtPrinter.cpp +++ clang/lib/AST/StmtPrinter.cpp @@ -1398,6 +1398,9 @@ OS << UnaryOperator::getOpcodeStr(Node->getOpcode()); } +void StmtPrinter::VisitBuiltinInstanceMemberExpr( + BuiltinInstanceMemberExpr *Node) {} + void StmtPrinter::VisitOffsetOfExpr(OffsetOfExpr *Node) { OS << "__builtin_offsetof("; Node->getTypeSourceInfo()->getType().print(OS, Policy); Index: clang/lib/AST/StmtProfile.cpp =================================================================== --- clang/lib/AST/StmtProfile.cpp +++ clang/lib/AST/StmtProfile.cpp @@ -1364,6 +1364,9 @@ ID.AddInteger(S->getOpcode()); } +void StmtProfiler::VisitBuiltinInstanceMemberExpr( + const BuiltinInstanceMemberExpr *S) {} + void StmtProfiler::VisitOffsetOfExpr(const OffsetOfExpr *S) { VisitType(S->getTypeSourceInfo()->getType()); unsigned n = S->getNumComponents(); Index: clang/lib/Analysis/ThreadSafetyCommon.cpp =================================================================== --- clang/lib/Analysis/ThreadSafetyCommon.cpp +++ clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -226,6 +226,28 @@ ClassifyDiagnostic(Exp->getType())}; } +static const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) { + if (const auto *V = dyn_cast(E)) + return V->clangDecl(); + if (const auto *Ph = dyn_cast(E)) + return Ph->clangDecl(); + if (const auto *P = dyn_cast(E)) + return P->clangDecl(); + if (const auto *L = dyn_cast(E)) + return L->clangDecl(); + return nullptr; +} + +static bool hasAnyPointerType(const til::SExpr *E) { + auto *VD = getValueDeclFromSExpr(E); + if (VD && VD->getType()->isAnyPointerType()) + return true; + if (const auto *C = dyn_cast(E)) + return C->castOpcode() == til::CAST_objToPtr; + + return false; +} + // Translate a clang statement or expression to a TIL expression. // Also performs substitution of variables; Ctx provides the context. // Dispatches on the type of S. @@ -243,6 +265,18 @@ return translateDeclRefExpr(cast(S), Ctx); case Stmt::CXXThisExprClass: return translateCXXThisExpr(cast(S), Ctx); + + case Stmt::BuiltinInstanceMemberExprClass: { + const BuiltinInstanceMemberExpr *BSME = cast(S); + til::SExpr *SelfExpr = translate(cast(Ctx->SelfArg), Ctx); + til::SExpr *BE = SelfExpr; + til::SExpr *E = new (Arena) til::SApply(BE); + + til::Project *P = new (Arena) til::Project(E, BSME->getFieldDecl()); + if (hasAnyPointerType(BE)) + P->setArrow(true); + return P; + } case Stmt::MemberExprClass: return translateMemberExpr(cast(S), Ctx); case Stmt::ObjCIvarRefExprClass: @@ -345,28 +379,6 @@ return SelfVar; } -static const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) { - if (const auto *V = dyn_cast(E)) - return V->clangDecl(); - if (const auto *Ph = dyn_cast(E)) - return Ph->clangDecl(); - if (const auto *P = dyn_cast(E)) - return P->clangDecl(); - if (const auto *L = dyn_cast(E)) - return L->clangDecl(); - return nullptr; -} - -static bool hasAnyPointerType(const til::SExpr *E) { - auto *VD = getValueDeclFromSExpr(E); - if (VD && VD->getType()->isAnyPointerType()) - return true; - if (const auto *C = dyn_cast(E)) - return C->castOpcode() == til::CAST_objToPtr; - - return false; -} - // Grab the very first declaration of virtual method D static const CXXMethodDecl *getFirstVirtualDecl(const CXXMethodDecl *D) { while (true) { Index: clang/lib/Parse/ParseExpr.cpp =================================================================== --- clang/lib/Parse/ParseExpr.cpp +++ clang/lib/Parse/ParseExpr.cpp @@ -1315,6 +1315,7 @@ break; case tok::kw___builtin_va_arg: case tok::kw___builtin_offsetof: + case tok::kw___builtin_instance_member: case tok::kw___builtin_choose_expr: case tok::kw___builtin_astype: // primary-expression: [OCL] as_type() case tok::kw___builtin_convertvector: @@ -2600,6 +2601,18 @@ Res = Actions.ActOnVAArg(StartLoc, Expr.get(), Ty.get(), ConsumeParen()); break; } + case tok::kw___builtin_instance_member: { + auto *II = Tok.getIdentifierInfo(); + ConsumeToken(); + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << tok::r_paren; + return ExprError(); + } + PT.consumeClose(); + + Res = Actions.ActOnBuiltinInstanceMember(II); + break; + } case tok::kw___builtin_offsetof: { SourceLocation TypeLoc = Tok.getLocation(); auto OOK = Sema::OffsetOfKind::OOK_Builtin; Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -16700,6 +16700,19 @@ Comps, Exprs, RParenLoc); } +ExprResult Sema::ActOnBuiltinInstanceMember(const IdentifierInfo *MemberII) { + const auto *RD = dyn_cast(getFunctionLevelDeclContext()); + assert(RD); // TODO: Diagnostic. + + auto It = llvm::find_if(RD->fields(), [MemberII](const FieldDecl *F) { + return F->getIdentifier() == MemberII; + }); + + assert(It != RD->fields().end()); // TODO: Diagnostic. + + return BuiltinInstanceMemberExpr::Create(Context, *It); +} + ExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, SourceLocation BuiltinLoc, SourceLocation TypeLoc, Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -10957,6 +10957,12 @@ SubExpr.get()); } +template +ExprResult TreeTransform::TransformBuiltinInstanceMemberExpr( + BuiltinInstanceMemberExpr *E) { + return ExprError(); +} + template ExprResult TreeTransform::TransformOffsetOfExpr(OffsetOfExpr *E) { Index: clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- clang/lib/Serialization/ASTReaderStmt.cpp +++ clang/lib/Serialization/ASTReaderStmt.cpp @@ -714,6 +714,9 @@ FPOptionsOverride::getFromOpaqueInt(Record.readInt())); } +void ASTStmtReader::VisitBuiltinInstanceMemberExpr( + BuiltinInstanceMemberExpr *E) {} + void ASTStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) { VisitExpr(E); assert(E->getNumComponents() == Record.peekInt()); Index: clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- clang/lib/Serialization/ASTWriterStmt.cpp +++ clang/lib/Serialization/ASTWriterStmt.cpp @@ -747,6 +747,9 @@ Code = serialization::EXPR_UNARY_OPERATOR; } +void ASTStmtWriter::VisitBuiltinInstanceMemberExpr( + BuiltinInstanceMemberExpr *E) {} + void ASTStmtWriter::VisitOffsetOfExpr(OffsetOfExpr *E) { VisitExpr(E); Record.push_back(E->getNumComponents()); Index: clang/test/Sema/warn-thread-safety-analysis.c =================================================================== --- clang/test/Sema/warn-thread-safety-analysis.c +++ clang/test/Sema/warn-thread-safety-analysis.c @@ -145,6 +145,24 @@ return 0; } +struct Holder { + struct Mutex *M; + int counter GUARDED_BY(__builtin_instance_member(M)); +}; + +static void lock_holder(struct Holder *H) __attribute__((acquire_capability(H->M))) NO_THREAD_SAFETY_ANALYSIS {} +static void unlock_holder(struct Holder *H) __attribute__((release_capability(H->M))) NO_THREAD_SAFETY_ANALYSIS {} + +static void test_holder(void) { + struct Holder H = {(void*)0, 0}; + + lock_holder(&H); + H.counter++; + unlock_holder(&H); + + H.counter--; // expected-warning {{requires holding mutex 'H.M'}} +} + // We had a problem where we'd skip all attributes that follow a late-parsed // attribute in a single __attribute__. void run(void) __attribute__((guarded_by(mu1), guarded_by(mu1))); // expected-warning 2{{only applies to non-static data members and global variables}}