tbaeder created this revision. tbaeder added reviewers: aaronpuchert, NoQ, aaron.ballman. Herald added a project: All. tbaeder requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
As discussed in https://github.com/llvm/llvm-project/issues/20777, this adds `__builtin_instance_member(membername)`, which acts like `this->membername`, but in C. This is obviously very much WIP and the patch contains several placeholders, but I wonder if this is the right approach to take here and if I should continue on this path, so I'm opening this for review. As you can see from the test case, this works for the (one) use case it was created for. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D153001 Files: clang/include/clang/AST/EvaluatedExprVisitor.h clang/include/clang/AST/Expr.h clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/Basic/StmtNodes.td clang/include/clang/Basic/TokenKinds.def clang/include/clang/Sema/Sema.h clang/lib/AST/Expr.cpp clang/lib/AST/StmtPrinter.cpp clang/lib/AST/StmtProfile.cpp clang/lib/Analysis/ThreadSafetyCommon.cpp clang/lib/Parse/ParseExpr.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/TreeTransform.h clang/lib/Serialization/ASTReaderStmt.cpp clang/lib/Serialization/ASTWriterStmt.cpp clang/test/Sema/warn-thread-safety-analysis.c
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}} 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/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/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -10957,6 +10957,12 @@ SubExpr.get()); } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformBuiltinInstanceMemberExpr( + BuiltinInstanceMemberExpr *E) { + return ExprError(); +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformOffsetOfExpr(OffsetOfExpr *E) { 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<RecordDecl>(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/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/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<til::Variable>(E)) + return V->clangDecl(); + if (const auto *Ph = dyn_cast<til::Phi>(E)) + return Ph->clangDecl(); + if (const auto *P = dyn_cast<til::Project>(E)) + return P->clangDecl(); + if (const auto *L = dyn_cast<til::LiteralPtr>(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<til::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<DeclRefExpr>(S), Ctx); case Stmt::CXXThisExprClass: return translateCXXThisExpr(cast<CXXThisExpr>(S), Ctx); + + case Stmt::BuiltinInstanceMemberExprClass: { + const BuiltinInstanceMemberExpr *BSME = cast<BuiltinInstanceMemberExpr>(S); + til::SExpr *SelfExpr = translate(cast<const Expr *>(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<MemberExpr>(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<til::Variable>(E)) - return V->clangDecl(); - if (const auto *Ph = dyn_cast<til::Phi>(E)) - return Ph->clangDecl(); - if (const auto *P = dyn_cast<til::Project>(E)) - return P->clangDecl(); - if (const auto *L = dyn_cast<til::LiteralPtr>(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<til::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/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/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/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/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6012,6 +6012,7 @@ ParsedType ParsedArgTy, ArrayRef<OffsetOfComponent> Components, SourceLocation RParenLoc); + ExprResult ActOnBuiltinInstanceMember(const IdentifierInfo *II); // __builtin_choose_expr(constExpr, expr1, expr2) ExprResult ActOnChooseExpr(SourceLocation BuiltinLoc, 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/Basic/StmtNodes.td =================================================================== --- clang/include/clang/Basic/StmtNodes.td +++ clang/include/clang/Basic/StmtNodes.td @@ -99,6 +99,7 @@ def GenericSelectionExpr : StmtNode<Expr>; def PseudoObjectExpr : StmtNode<Expr>; def SourceLocExpr : StmtNode<Expr>; +def BuiltinInstanceMemberExpr : StmtNode<Expr>; // Wrapper expressions def FullExpr : StmtNode<Expr, 1>; 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/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/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) { }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits