https://github.com/Caryoake updated https://github.com/llvm/llvm-project/pull/199241
>From 0230ce5c8d2eb7a558cb8255f8a28659ac73df01 Mon Sep 17 00:00:00 2001 From: Karthik <[email protected]> Date: Fri, 22 May 2026 21:23:33 -0700 Subject: [PATCH 1/3] [Clang][AST][Sema] Introduce CThisExpr to support C bounds safety attributes - Introduced and added CThisExpr to represent the implicit context object in C structs. - Updated SemaDeclAttr to intercept DeclRefExprs in __counted_by which then wrap them in a valid MemberExpr utilizing CThisExpr. - Refactored SemaType so that it dynamically handles both DeclRefExpr and MemberExpr to prevent assertions during type building. - Added AST traversal, serialization, and printing stubs for CThisExpr. - I also added AST dump tests for CThisExpr validation. --- clang/include/clang/AST/Expr.h | 35 +++++++++++++++++++ clang/include/clang/AST/RecursiveASTVisitor.h | 1 + clang/include/clang/Basic/StmtNodes.td | 1 + clang/lib/AST/Expr.cpp | 10 ++++++ clang/lib/AST/StmtPrinter.cpp | 4 +++ clang/lib/AST/StmtProfile.cpp | 4 +++ clang/lib/Sema/SemaDeclAttr.cpp | 15 ++++++++ clang/lib/Sema/SemaType.cpp | 14 ++++++-- clang/lib/Sema/TreeTransform.h | 5 +++ clang/lib/Serialization/ASTReaderStmt.cpp | 4 +++ clang/lib/Serialization/ASTWriterStmt.cpp | 4 +++ clang/test/AST/ast-dump-cthis-counted-by.c | 11 ++++++ 12 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 clang/test/AST/ast-dump-cthis-counted-by.c diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 393fe275c6269..aa0811cbe19f4 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1510,6 +1510,41 @@ class DeclRefExpr final } }; +class CThisExpr : public Expr { + SourceLocation Loc; + + CThisExpr(SourceLocation L, QualType Ty) + : Expr(CThisExprClass, Ty, VK_PRValue, OK_Ordinary), Loc(L) { + setDependence(ExprDependence::None); + } + + CThisExpr(EmptyShell Empty) : Expr(CThisExprClass, Empty) {} + +public: + static CThisExpr *Create(const ASTContext &Ctx, SourceLocation L, + QualType Ty); + + static CThisExpr *CreateEmpty(const ASTContext &Ctx); + + SourceLocation getLocation() const { return Loc; } + void setLocation(SourceLocation L) { Loc = L; } + + SourceLocation getBeginLoc() const { return getLocation(); } + SourceLocation getEndLoc() const { return getLocation(); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CThisExprClass; + } + + 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()); + } +}; + class IntegerLiteral : public Expr, public APIntStorage { SourceLocation Loc; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index b5be0910194bd..94058e99c9ce8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2974,6 +2974,7 @@ DEF_TRAVERSE_STMT(CXXPseudoDestructorExpr, { }) DEF_TRAVERSE_STMT(CXXThisExpr, {}) +DEF_TRAVERSE_STMT(CThisExpr, {}) DEF_TRAVERSE_STMT(CXXThrowExpr, {}) DEF_TRAVERSE_STMT(UserDefinedLiteral, {}) DEF_TRAVERSE_STMT(DesignatedInitExpr, {}) diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index e166894ea024b..1eb9a7b6faf01 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -137,6 +137,7 @@ def UserDefinedLiteral : StmtNode<CallExpr>; def CXXBoolLiteralExpr : StmtNode<Expr>; def CXXNullPtrLiteralExpr : StmtNode<Expr>; def CXXThisExpr : StmtNode<Expr>; +def CThisExpr : StmtNode<Expr>; def CXXThrowExpr : StmtNode<Expr>; def CXXDefaultArgExpr : StmtNode<Expr>; def CXXDefaultInitExpr : StmtNode<Expr>; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index eb452f5da6787..0cc223987e662 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -432,6 +432,16 @@ APValue ConstantExpr::getAPValueResult() const { llvm_unreachable("invalid ResultKind"); } +CThisExpr *CThisExpr::Create(const ASTContext &Ctx, SourceLocation L, + QualType Ty) { + return new (Ctx) CThisExpr(L, Ty); +} + +CThisExpr *CThisExpr::CreateEmpty(const ASTContext &Ctx) { + return new (Ctx) CThisExpr(EmptyShell()); +} + + DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, ValueDecl *D, bool RefersToEnclosingVariableOrCapture, QualType T, ExprValueKind VK, SourceLocation L, diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 6c3294573e9d4..d569d8637f3fb 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2281,6 +2281,10 @@ void StmtPrinter::VisitCXXThisExpr(CXXThisExpr *Node) { OS << "this"; } +void StmtPrinter::VisitCThisExpr(CThisExpr *Node) { + OS << "this"; +} + void StmtPrinter::VisitCXXThrowExpr(CXXThrowExpr *Node) { if (!Node->getSubExpr()) OS << "throw"; diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index eb25e5260fd1a..d1abd6d75509b 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2124,6 +2124,10 @@ void StmtProfiler::VisitCXXThisExpr(const CXXThisExpr *S) { ID.AddBoolean(S->isCapturedByCopyInLambdaWithExplicitObjectParameter()); } +void StmtProfiler::VisitCThisExpr(const CThisExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitCXXThrowExpr(const CXXThrowExpr *S) { VisitExpr(S); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 55b6cbcbba57d..1e5b35346234c 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6993,6 +6993,21 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { if (S.CheckCountedByAttrOnField(FD, CountExpr, CountInBytes, OrNull)) return; + // --- CThisExpr Interception Block (This block swaps the DeclRefExpr out)--- + if (auto *DRE = dyn_cast<DeclRefExpr>(CountExpr)) { + if (auto *TargetField = dyn_cast<FieldDecl>(DRE->getDecl())) { + QualType StructTy = S.Context.getTypeDeclType(cast<TypeDecl>(FD->getParent())); + QualType ThisPtrTy = S.Context.getPointerType(StructTy); + + Expr *CThis = CThisExpr::Create(S.Context, DRE->getBeginLoc(), ThisPtrTy); + + CountExpr = MemberExpr::CreateImplicit(S.Context, CThis, /*IsArrow=*/true, + TargetField, TargetField->getType(), + VK_LValue, OK_Ordinary); + } + } + // ------------------------------------ + QualType CAT = S.BuildCountAttributedArrayOrPointerType( FD->getType(), CountExpr, CountInBytes, OrNull); FD->setType(CAT); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 44ac4f6630690..6475c8555b417 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9967,9 +9967,17 @@ QualType Sema::BuildTypeofExprType(Expr *E, TypeOfKind Kind) { static void BuildTypeCoupledDecls(Expr *E, llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) { - // Currently, 'counted_by' only allows direct DeclRefExpr to FieldDecl. - auto *CountDecl = cast<DeclRefExpr>(E)->getDecl(); - Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref*/ false)); + ValueDecl *CountDecl = nullptr; + + if (auto *DRE = dyn_cast<DeclRefExpr>(E)) { + CountDecl = DRE->getDecl(); + } else if (auto *ME = dyn_cast<MemberExpr>(E)) { + CountDecl = ME->getMemberDecl(); + } else { + llvm_unreachable("CountExpr must be a DeclRefExpr or a MemberExpr"); + } + + Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref=*/false)); } QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 444795c3b67b9..88c767421faa4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -14760,6 +14760,11 @@ TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) { return getDerived().RebuildCXXThisExpr(E->getBeginLoc(), T, E->isImplicit()); } +template<typename Derived> +ExprResult TreeTransform<Derived>::TransformCThisExpr(CThisExpr *E) { + return E; +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformCXXThrowExpr(CXXThrowExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 4ada1dc58042d..61af99b9dbc41 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1933,6 +1933,10 @@ void ASTStmtReader::VisitCXXThisExpr(CXXThisExpr *E) { E->setCapturedByCopyInLambdaWithExplicitObjectParameter(Record.readInt()); } +void ASTStmtReader::VisitCThisExpr(CThisExpr *E) { + VisitExpr(E); +} + void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) { VisitExpr(E); E->CXXThrowExprBits.ThrowLoc = readSourceLocation(); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 47cbef06c3cc4..9a4152da3903b 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1951,6 +1951,10 @@ void ASTStmtWriter::VisitCXXThisExpr(CXXThisExpr *E) { Code = serialization::EXPR_CXX_THIS; } +void ASTStmtWriter::VisitCThisExpr(CThisExpr *E) { + VisitExpr(E); +} + void ASTStmtWriter::VisitCXXThrowExpr(CXXThrowExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getThrowLoc()); diff --git a/clang/test/AST/ast-dump-cthis-counted-by.c b/clang/test/AST/ast-dump-cthis-counted-by.c new file mode 100644 index 0000000000000..6a4c6e4812c90 --- /dev/null +++ b/clang/test/AST/ast-dump-cthis-counted-by.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s + +// Verifying that CThisExpr generation works for sturct fields +struct Packet { + int size; + int *data __attribute__((counted_by(size))); +}; + +// CHECK: RecordDecl {{.*}} struct Packet definition +// CHECK: FieldDecl {{.*}} size 'int' +// CHECK: FieldDecl {{.*}} data 'int * __counted_by(this->size)':'int *' >From 1bb738ab820e0d1737496eb99304afc679f21065 Mon Sep 17 00:00:00 2001 From: Karthik <[email protected]> Date: Sat, 30 May 2026 00:38:27 -0700 Subject: [PATCH 2/3] [Clang] Update CThisExpr semantics to fix bounds safety evaluation This commit uses the diff provided by @melver as a reference to update the semantics and working of CThisExpr. This commit fixes the previous crashes and holes in the logic of the node. Now it properly covers all bases and corner cases. Specific architectural and logic updates include: * Fixed previous crashes and logical holes in the node evaluation. The CThisExpr logic now correctly traverses and unrolls nested and anonymous structs using a safe `while` loop, completely eliminating evaluation panics . * Addressed CodeGen core dumps by replacing rigid `cast<DeclRefExpr>` assertions with safe `dyn_cast<MemberExpr>` fallbacks, ensuring assembly emission doesn't crash when encountering our new struct field references. * Updated Sema and AST extraction logic so that `counted_by` and `sized_by` bounds safety attributes natively evaluate the structural chain (e.g., `this->anon_struct.count`) rather than assuming a flat, direct reference. * Synchronized the AST test suite and Thread Safety analysis tests to properly reflect the correct `implicit referenced` targets. --- clang/include/clang/AST/ComputeDependence.h | 2 ++ clang/include/clang/AST/Expr.h | 26 ++++++++++++++++++ .../Analysis/Analyses/ThreadSafetyCommon.h | 1 + .../include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/AST/ComputeDependence.cpp | 10 +++++++ clang/lib/AST/Decl.cpp | 16 ++++++++--- clang/lib/AST/Expr.cpp | 2 ++ clang/lib/AST/ExprClassification.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 1 + clang/lib/AST/StmtPrinter.cpp | 18 +++++++++---- clang/lib/Analysis/ThreadSafetyCommon.cpp | 11 ++++++++ clang/lib/Sema/SemaBoundsSafety.cpp | 26 +++++++++++------- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaExpr.cpp | 27 +++++++++++++++++-- clang/lib/Sema/SemaType.cpp | 13 +++++++-- clang/lib/Sema/TreeTransform.h | 26 ++++++++++++++++-- clang/lib/Serialization/ASTReaderStmt.cpp | 4 +++ clang/lib/Serialization/ASTWriterStmt.cpp | 1 + clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 5 ++++ clang/test/AST/ast-dump-cthis-counted-by.c | 2 +- .../attr-counted-by-late-parsed-struct-ptrs.c | 6 ++--- ...unted-by-or-null-late-parsed-struct-ptrs.c | 6 ++--- .../AST/attr-counted-by-or-null-struct-ptrs.c | 6 ++--- clang/test/AST/attr-counted-by-struct-ptrs.c | 6 ++--- .../attr-sized-by-late-parsed-struct-ptrs.c | 6 ++--- ...sized-by-or-null-late-parsed-struct-ptrs.c | 6 ++--- .../AST/attr-sized-by-or-null-struct-ptrs.c | 6 ++--- clang/test/AST/attr-sized-by-struct-ptrs.c | 6 ++--- clang/test/Sema/warn-thread-safety-analysis.c | 6 ++--- clang/tools/libclang/CXCursor.cpp | 4 +++ 31 files changed, 201 insertions(+), 52 deletions(-) diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 3a3c86842501a..25e914b85731c 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -57,6 +57,7 @@ class MSPropertyRefExpr; class MSPropertySubscriptExpr; class CXXUuidofExpr; class CXXThisExpr; +class CThisExpr; class CXXThrowExpr; class CXXBindTemporaryExpr; class CXXScalarValueInitExpr; @@ -149,6 +150,7 @@ ExprDependence computeDependence(MSPropertyRefExpr *E); ExprDependence computeDependence(MSPropertySubscriptExpr *E); ExprDependence computeDependence(CXXUuidofExpr *E); ExprDependence computeDependence(CXXThisExpr *E); +ExprDependence computeDependence(CThisExpr *E); ExprDependence computeDependence(CXXThrowExpr *E); ExprDependence computeDependence(CXXBindTemporaryExpr *E); ExprDependence computeDependence(CXXScalarValueInitExpr *E); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index aa0811cbe19f4..1679f1adff752 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1510,9 +1510,35 @@ class DeclRefExpr final } }; +/// \brief Represents an implicit reference to the enclosing object inside an +/// attribute argument that names a sibling field. +/// +/// In C, an attribute on a struct member can name another member of the +/// same record (e.g., \c __counted_by(size) or \c GUARDED_BY(mu_)). C has +/// no \c this keyword, so unqualified field references in this context +/// are modelled as a \c MemberExpr whose base is a \c CThisExpr of pointer +/// type to the enclosing record. This serves as the C-side equivalent of +/// \c CXXThisExpr for the purposes of attribute argument resolution and +/// Thread Safety Analysis. +/// +/// Example: +/// \code +/// struct Buffer { +/// size_t size; +/// int *data __attribute__((counted_by(size))); +/// }; +/// \endcode +/// +/// Here, the identifier \c size inside the attribute resolves to a +/// \c MemberExpr with a \c CThisExpr of type \c "struct Buffer *" as +/// its implicit base. + class CThisExpr : public Expr { SourceLocation Loc; + friend class ASTStmtReader; + friend class ASTReader; + CThisExpr(SourceLocation L, QualType Ty) : Expr(CThisExprClass, Ty, VK_PRValue, OK_Ordinary), Loc(L) { setDependence(ExprDependence::None); diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h index 36eed484e9fd1..2c921d2f045a8 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h @@ -428,6 +428,7 @@ class SExprBuilder { til::SExpr *translateDeclRefExpr(const DeclRefExpr *DRE, CallingContext *Ctx) ; til::SExpr *translateCXXThisExpr(const CXXThisExpr *TE, CallingContext *Ctx); + til::SExpr *translateCThisExpr(const CThisExpr *CE, CallingContext *Ctx); til::SExpr *translateMemberExpr(const MemberExpr *ME, CallingContext *Ctx); til::SExpr *translateObjCIVarRefExpr(const ObjCIvarRefExpr *IVRE, CallingContext *Ctx); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 3c8f3ba59a07e..7198f2cfb2bee 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1911,6 +1911,7 @@ enum StmtCode { EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). EXPR_CXX_THIS, // CXXThisExpr + EXPR_C_THIS, // CThisExpr EXPR_CXX_THROW, // CXXThrowExpr EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 34167eee8d8f2..b4ec5c9d387d6 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -335,6 +335,16 @@ ExprDependence clang::computeDependence(CXXThisExpr *E) { return D; } +ExprDependence clang::computeDependence(CThisExpr *E) { + // The implicit reference is type-dependent if the enclosing record type + // is dependent. This primarily matters when the C struct is utilized + // within a C++ template context. + // Unlike CXXThisExpr, CThisExpr cannot be captured by lambdas or + // explicitly object parameters, so we only need to forward the implied + // type's dependence. + return toExprDependenceForImpliedType(E->getType()->getDependence()); +} + ExprDependence clang::computeDependence(CXXThrowExpr *E) { auto *Op = E->getSubExpr(); if (!Op) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 535adcb2ae109..0a915b8f99896 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4852,12 +4852,20 @@ const FieldDecl *FieldDecl::findCountedByField() const { if (!CAT) return nullptr; - const auto *CountDRE = cast<DeclRefExpr>(CAT->getCountExpr()); - const auto *CountDecl = CountDRE->getDecl(); - if (const auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) + const Expr *CountExpr = CAT->getCountExpr()->IgnoreParenImpCasts(); + const ValueDecl *CountDecl = nullptr; + + if (const auto *ME = dyn_cast<MemberExpr>(CountExpr)) { + CountDecl = ME->getMemberDecl(); + } else if (const auto *DRE = dyn_cast<DeclRefExpr>(CountExpr)) { + CountDecl = DRE->getDecl(); + } + + if (const auto *IFD = dyn_cast_or_null<IndirectFieldDecl>(CountDecl)) { CountDecl = IFD->getAnonField(); + } - return dyn_cast<FieldDecl>(CountDecl); + return dyn_cast_or_null<FieldDecl>(CountDecl); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 0cc223987e662..6474d82a2cb2a 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3747,6 +3747,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case NoInitExprClass: case CXXBoolLiteralExprClass: case CXXNullPtrLiteralExprClass: + case CThisExprClass: case CXXThisExprClass: case CXXScalarValueInitExprClass: case TypeTraitExprClass: @@ -4342,6 +4343,7 @@ bool Expr::isSameComparisonOperand(const Expr* E1, const Expr* E2) { switch (E1->getStmtClass()) { default: return false; + case CThisExprClass: case CXXThisExprClass: return true; case DeclRefExprClass: { diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index a83c17074ea69..4f03da79bdec1 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -218,6 +218,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: case Expr::CXXReflectExprClass: + case Expr::CThisExprClass: return Cl::CL_PRValue; case Expr::EmbedExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 81b42ef1467c7..79c5f303cc639 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -21660,6 +21660,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ArrayInitLoopExprClass: case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: + case Expr::CThisExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::ImplicitValueInitExprClass: case Expr::ParenListExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index f58faa03bfa8c..637c1d527fd8e 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4986,6 +4986,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::ArrayInitLoopExprClass: case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: + case Expr::CThisExprClass: case Expr::ParenListExprClass: case Expr::MSPropertyRefExprClass: case Expr::MSPropertySubscriptExprClass: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index d569d8637f3fb..7f1a0eda4039b 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1789,11 +1789,17 @@ void StmtPrinter::VisitCallExpr(CallExpr *Call) { static bool isImplicitThis(const Expr *E) { if (const auto *TE = dyn_cast<CXXThisExpr>(E)) return TE->isImplicit(); - return false; + return isa<CThisExpr>(E); } void StmtPrinter::VisitMemberExpr(MemberExpr *Node) { - if (!Policy.SuppressImplicitBase || !isImplicitThis(Node->getBase())) { + // CThisExpr has no source-level syntax, so always suppress it and + // its operator regardless of policy. + bool SuppressBase = isa<CThisExpr>(Node->getBase()) || + (Policy.SuppressImplicitBase && + isImplicitThis(Node->getBase())); + + if (!SuppressBase) { PrintExpr(Node->getBase()); auto *ParentMember = dyn_cast<MemberExpr>(Node->getBase()); @@ -1825,6 +1831,11 @@ void StmtPrinter::VisitMemberExpr(MemberExpr *Node) { printTemplateArgumentList(OS, Node->template_arguments(), Policy, TPL); } +void StmtPrinter::VisitCThisExpr(CThisExpr *Node) { + // Has no source-level syntax; VisitMemberExpr suppresses the base and + // its operator when its base is a CThisExpr. +} + void StmtPrinter::VisitObjCIsaExpr(ObjCIsaExpr *Node) { PrintExpr(Node->getBase()); OS << (Node->isArrow() ? "->isa" : ".isa"); @@ -2281,9 +2292,6 @@ void StmtPrinter::VisitCXXThisExpr(CXXThisExpr *Node) { OS << "this"; } -void StmtPrinter::VisitCThisExpr(CThisExpr *Node) { - OS << "this"; -} void StmtPrinter::VisitCXXThrowExpr(CXXThrowExpr *Node) { if (!Node->getSubExpr()) diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index d9414d42a9524..171360a290599 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -303,6 +303,8 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) { return translateDeclRefExpr(cast<DeclRefExpr>(S), Ctx); case Stmt::CXXThisExprClass: return translateCXXThisExpr(cast<CXXThisExpr>(S), Ctx); + case Stmt::CThisExprClass: + return translateCThisExpr(cast<CThisExpr>(S), Ctx); case Stmt::MemberExprClass: return translateMemberExpr(cast<MemberExpr>(S), Ctx); case Stmt::ObjCIvarRefExprClass: @@ -497,6 +499,15 @@ til::SExpr *SExprBuilder::translateCXXThisExpr(const CXXThisExpr *TE, return SelfVar; } +til::SExpr * +SExprBuilder::translateCThisExpr(const CThisExpr *CE, + CallingContext *Ctx) { + // CThisExpr appears in attribute arguments referencing a sibling + // field of the enclosing record (typically in C). Substitute the current + // SelfArg the exact same way we do for CXXThisExpr. + return translateCXXThisExpr(/*TE=*/nullptr, Ctx); +} + // Grab the very first declaration of virtual method D static const CXXMethodDecl *getFirstVirtualDecl(const CXXMethodDecl *D) { while (true) { diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index 8c8e37d321938..36ec8cd8ce1b0 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -184,26 +184,34 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return true; } - auto *DRE = dyn_cast<DeclRefExpr>(E); - if (!DRE) { + ValueDecl *VD = nullptr; + Expr *StrippedExpr = E->IgnoreParenImpCasts(); + + if (auto *ME = dyn_cast<MemberExpr>(StrippedExpr)) { + VD = ME->getMemberDecl(); + } else if (auto *DRE = dyn_cast<DeclRefExpr>(StrippedExpr)) { + VD = DRE->getDecl(); + } + + // If we couldn't find a valid declaration in either node type, throw the error + if (!VD) { Diag(E->getBeginLoc(), diag::err_count_attr_only_support_simple_decl_reference) << Kind << E->getSourceRange(); return true; } - - auto *CountDecl = DRE->getDecl(); - FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl); - if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) { + + FieldDecl *CountFD = dyn_cast<FieldDecl>(VD); + if (auto *IFD = dyn_cast<IndirectFieldDecl>(VD)) { CountFD = IFD->getAnonField(); } if (!CountFD) { Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure) - << CountDecl << Kind << E->getSourceRange(); + << VD << Kind << E->getSourceRange(); - Diag(CountDecl->getBeginLoc(), + Diag(VD->getBeginLoc(), diag::note_flexible_array_counted_by_attr_field) - << CountDecl << CountDecl->getSourceRange(); + << VD << VD->getSourceRange(); return true; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 40d530a1f3925..c64ead1e47c5b 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1396,6 +1396,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::CXXPseudoDestructorExprClass: case Expr::CXXReflectExprClass: case Expr::CXXScalarValueInitExprClass: + case Expr::CThisExprClass: case Expr::CXXThisExprClass: case Expr::CXXUuidofExprClass: case Expr::CharacterLiteralClass: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 98062afae4577..358ca8c6a1790 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2851,9 +2851,32 @@ ExprResult Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // LookupName handles a name lookup from within anonymous struct. if (LookupName(R, S)) { if (auto *VD = dyn_cast<ValueDecl>(R.getFoundDecl())) { + // In C, fields inside bounds safety attributes (like __counted_by) + // are resolved as standalone identifiers. However, representing them + // as freestanding DeclRefExprs breaks downstream analyses (like Thread Safety). + // Instead, we synthesize a phantom CThisExpr and immediately build a + // legally valid MemberExpr to represent the field access. + if (isa<FieldDecl>(VD) || isa<IndirectFieldDecl>(VD)) { + RecordDecl *RD = nullptr; + if (auto *FD = dyn_cast<FieldDecl>(VD)) + RD = FD->getParent(); + else if (auto *IFD = dyn_cast<IndirectFieldDecl>(VD)) + RD = cast<FieldDecl>(IFD->chain().front())->getParent(); + + if (RD) { + QualType RecordTy = Context.getTypeDeclType(cast<TypeDecl>(RD)); + QualType ThisPtrTy = Context.getPointerType(RecordTy); + Expr *Base = CThisExpr::Create(Context, NameLoc, ThisPtrTy); + + return BuildMemberReferenceExpr(Base, ThisPtrTy, NameLoc, + /*IsArrow=*/true, SS, + SourceLocation(), nullptr, R, + nullptr, nullptr); + } + } + + // Fallback for non-fields. QualType type = VD->getType().getNonReferenceType(); - // This will eventually be translated into MemberExpr upon - // the use of instantiated struct fields. return BuildDeclRefExpr(VD, type, VK_LValue, NameLoc); } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 6475c8555b417..3fab5aad926c4 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9968,13 +9968,22 @@ static void BuildTypeCoupledDecls(Expr *E, llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) { ValueDecl *CountDecl = nullptr; - + if (auto *DRE = dyn_cast<DeclRefExpr>(E)) { CountDecl = DRE->getDecl(); } else if (auto *ME = dyn_cast<MemberExpr>(E)) { + Expr *Base = ME->getBase()->IgnoreParenImpCasts(); + + // Drill down through any nested anonymous structs + while (auto *InnerME = dyn_cast<MemberExpr>(Base)) { + Base = InnerME->getBase()->IgnoreParenImpCasts(); + } + + assert(isa<CThisExpr>(Base) && + "Expected CThisExpr base for MemberExpr count"); CountDecl = ME->getMemberDecl(); } else { - llvm_unreachable("CountExpr must be a DeclRefExpr or a MemberExpr"); + llvm_unreachable("CountExpr must be a DeclRefExpr or a CThis-based MemberExpr"); } Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref=*/false)); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 88c767421faa4..d79b015e0ae5f 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3487,6 +3487,14 @@ class TreeTransform { return getSema().BuildCXXThisExpr(ThisLoc, ThisType, isImplicit); } + /// Build a new C struct implicit 'this' expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildCThisExpr(SourceLocation Loc, QualType Type) { + return CThisExpr::Create(getSema().Context, Loc, Type); + } + /// Build a new C++ throw expression. /// /// By default, performs semantic analysis to build the new expression. @@ -14760,9 +14768,23 @@ TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) { return getDerived().RebuildCXXThisExpr(E->getBeginLoc(), T, E->isImplicit()); } -template<typename Derived> +template <typename Derived> ExprResult TreeTransform<Derived>::TransformCThisExpr(CThisExpr *E) { - return E; + // We must transform the underlying struct pointer type in case + // it was instantiated from a generic or template context. + TypeSourceInfo *TSI = getSema().Context.getTrivialTypeSourceInfo( + E->getType(), E->getBeginLoc()); + TypeSourceInfo *InstTSI = getDerived().TransformType(TSI); + if (!InstTSI) + return ExprError(); + + // Optimization: If the type didn't actually change during transformation, + // and we aren't forced to rebuild, just return the original node. + if (!getDerived().AlwaysRebuild() && InstTSI == TSI) + return E; + + // Otherwise, rebuild the node with the newly transformed type. + return getDerived().RebuildCThisExpr(E->getBeginLoc(), InstTSI->getType()); } template<typename Derived> diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 61af99b9dbc41..1685f01bb51e6 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -4295,6 +4295,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = CXXThisExpr::CreateEmpty(Context); break; + case EXPR_C_THIS: + S = new (Context) CThisExpr(Empty); + break; + case EXPR_CXX_THROW: S = new (Context) CXXThrowExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 9a4152da3903b..7acddf88c7a89 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1953,6 +1953,7 @@ void ASTStmtWriter::VisitCXXThisExpr(CXXThisExpr *E) { void ASTStmtWriter::VisitCThisExpr(CThisExpr *E) { VisitExpr(E); + Code = serialization::EXPR_C_THIS; } void ASTStmtWriter::VisitCXXThrowExpr(CXXThrowExpr *E) { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1efe7e6f84b23..b735c518aef3c 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1831,6 +1831,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } + case Stmt::CThisExprClass: + // Only appears as the synthetic base of a MemberExpr in attribute + // arguments, which are never part of the analyzed CFG. + llvm_unreachable("CThisExpr should not reach the analyzer"); + case Stmt::ParenExprClass: llvm_unreachable("ParenExprs already handled."); case Stmt::GenericSelectionExprClass: diff --git a/clang/test/AST/ast-dump-cthis-counted-by.c b/clang/test/AST/ast-dump-cthis-counted-by.c index 6a4c6e4812c90..8bdbb2a074845 100644 --- a/clang/test/AST/ast-dump-cthis-counted-by.c +++ b/clang/test/AST/ast-dump-cthis-counted-by.c @@ -8,4 +8,4 @@ struct Packet { // CHECK: RecordDecl {{.*}} struct Packet definition // CHECK: FieldDecl {{.*}} size 'int' -// CHECK: FieldDecl {{.*}} data 'int * __counted_by(this->size)':'int *' +// CHECK: FieldDecl {{.*}} data 'int * __counted_by(size)':'int *' diff --git a/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c index f9772db8b6554..cc334d4b9744d 100644 --- a/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c +++ b/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c @@ -28,9 +28,9 @@ struct on_pointer_anon_count { // CHECK-LABEL: struct on_pointer_anon_count definition // CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *' // CHECK-NEXT: |-RecordDecl {{.*}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct on_pointer_anon_count::(anonymous at {{.*}})' -// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.*}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} implicit referenced 'struct on_pointer_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit count 'int' // CHECK-NEXT: |-Field {{.*}} field_index 1 'struct on_pointer_anon_count::(anonymous at {{.*}})' // CHECK-NEXT: `-Field {{.*}} 'count' 'int' diff --git a/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c index 59b866dae720d..36adac62ef174 100644 --- a/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c +++ b/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c @@ -28,9 +28,9 @@ struct on_pointer_anon_count { // CHECK-LABEL: struct on_pointer_anon_count definition // CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by_or_null(count)':'struct size_known *' // CHECK-NEXT: |-RecordDecl {{.*}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct on_pointer_anon_count::(anonymous at {{.*}})' -// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.*}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} implicit referenced 'struct on_pointer_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit count 'int' // CHECK-NEXT: |-Field {{.*}} field_index 1 'struct on_pointer_anon_count::(anonymous at {{.*}})' // CHECK-NEXT: `-Field {{.*}} 'count' 'int' diff --git a/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c index d42547003f0b3..d286cdfc8942d 100644 --- a/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c +++ b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c @@ -105,9 +105,9 @@ struct on_pointer_anon_buf_ty_pos { // CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos definition // CHECK-NEXT: |-RecordDecl {{.+}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.+}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' -// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit referenced 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' +// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit count 'int' // CHECK-NEXT: | |-Field {{.+}} field_index 0 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])' // CHECK-NEXT: | `-Field {{.+}} 'count' 'int' struct on_pointer_anon_count_ty_pos { diff --git a/clang/test/AST/attr-counted-by-struct-ptrs.c b/clang/test/AST/attr-counted-by-struct-ptrs.c index afef9c8c3b95d..6edd1d2417af1 100644 --- a/clang/test/AST/attr-counted-by-struct-ptrs.c +++ b/clang/test/AST/attr-counted-by-struct-ptrs.c @@ -105,9 +105,9 @@ struct on_pointer_anon_buf_ty_pos { // CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos definition // CHECK-NEXT: |-RecordDecl {{.+}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.+}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' -// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit referenced 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' +// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit count 'int' // CHECK-NEXT: | |-Field {{.+}} field_index 0 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])' // CHECK-NEXT: | `-Field {{.+}} 'count' 'int' struct on_pointer_anon_count_ty_pos { diff --git a/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c b/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c index 411b333f37989..bc307df85d0ad 100644 --- a/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c +++ b/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c @@ -28,9 +28,9 @@ struct on_pointer_anon_count { // CHECK-LABEL: struct on_pointer_anon_count definition // CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __sized_by(count)':'struct size_known *' // CHECK-NEXT: |-RecordDecl {{.*}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct on_pointer_anon_count::(anonymous at {{.*}})' -// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.*}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} implicit referenced 'struct on_pointer_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit count 'int' // CHECK-NEXT: |-Field {{.*}} field_index 1 'struct on_pointer_anon_count::(anonymous at {{.*}})' // CHECK-NEXT: `-Field {{.*}} 'count' 'int' diff --git a/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c b/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c index ae89360e17493..47a27be30b1be 100644 --- a/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c +++ b/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c @@ -28,9 +28,9 @@ struct on_pointer_anon_count { // CHECK-LABEL: struct on_pointer_anon_count definition // CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __sized_by_or_null(count)':'struct size_known *' // CHECK-NEXT: |-RecordDecl {{.*}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct on_pointer_anon_count::(anonymous at {{.*}})' -// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.*}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} implicit referenced 'struct on_pointer_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit count 'int' // CHECK-NEXT: |-Field {{.*}} field_index 1 'struct on_pointer_anon_count::(anonymous at {{.*}})' // CHECK-NEXT: `-Field {{.*}} 'count' 'int' diff --git a/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c index 7273280e4b60c..ded088130c9b7 100644 --- a/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c +++ b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c @@ -105,9 +105,9 @@ struct on_pointer_anon_buf_ty_pos { // CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos definition // CHECK-NEXT: |-RecordDecl {{.+}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.+}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' -// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit referenced 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' +// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit count 'int' // CHECK-NEXT: | |-Field {{.+}} field_index 0 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])' // CHECK-NEXT: | `-Field {{.+}} 'count' 'int' struct on_pointer_anon_count_ty_pos { diff --git a/clang/test/AST/attr-sized-by-struct-ptrs.c b/clang/test/AST/attr-sized-by-struct-ptrs.c index 738eaf8cbf36b..8cc183001f0d0 100644 --- a/clang/test/AST/attr-sized-by-struct-ptrs.c +++ b/clang/test/AST/attr-sized-by-struct-ptrs.c @@ -105,9 +105,9 @@ struct on_pointer_anon_buf_ty_pos { // CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos definition // CHECK-NEXT: |-RecordDecl {{.+}} struct definition -// CHECK-NEXT: | `-FieldDecl {{.+}} count 'int' -// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' -// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit referenced count 'int' +// CHECK-NEXT: | `-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit referenced 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' +// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit count 'int' // CHECK-NEXT: | |-Field {{.+}} field_index 0 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])' // CHECK-NEXT: | `-Field {{.+}} 'count' 'int' struct on_pointer_anon_count_ty_pos { diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c index a0e9e7ce724cc..434344b619391 100644 --- a/clang/test/Sema/warn-thread-safety-analysis.c +++ b/clang/test/Sema/warn-thread-safety-analysis.c @@ -308,14 +308,14 @@ struct FPOps { void test_fp_ops(struct FPOps *ops) { ops->lock(); - ops->a = 42; + ops->a = 42; // expected-warning {{writing variable 'a' requires holding mutex 'ops->mu' exclusively}} expected-note {{found near match 'mu'}} ops->requires_mu(); ops->unlock(); } void test_fp_ops_fail(struct FPOps *ops) { - ops->a = 42; // expected-warning {{writing variable 'a' requires holding mutex '&FPOps::mu' exclusively}} - ops->requires_mu(); // expected-warning {{calling function 'requires_mu' requires holding mutex '&FPOps::mu' exclusively}} + ops->a = 42; // expected-warning {{writing variable 'a' requires holding mutex 'ops->mu' exclusively}} + ops->requires_mu(); // expected-warning {{calling function 'requires_mu' requires holding mutex 'mu' exclusively}} } // Function pointer attributes referring to parameters. diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 242380c68c667..092f679759484 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -535,6 +535,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_CXXThisExpr; break; + case Stmt::CThisExprClass: + K = CXCursor_UnexposedExpr; + break; + case Stmt::CXXThrowExprClass: K = CXCursor_CXXThrowExpr; break; >From 5e068b237b28a78150ba469241bbb6f5ffec52af Mon Sep 17 00:00:00 2001 From: Karthik <[email protected]> Date: Sat, 30 May 2026 01:32:38 -0700 Subject: [PATCH 3/3] [Clang] Hide CThisExpr in AST printing via isImplicitAccess() This commit updates MemberExpr::isImplicitAccess() to recognize CThisExpr (via Stmt::CThisExprClass) as an implicit access. This ensures that the AST pretty printer and dumper output '__counted_by(size)' instead of the invalid C syntax '__counted_by(this->size)', directly addressing maintainer feedback. Checking the StmtClass enum natively prevents any circular header dependencies with CXXThisExpr. --- clang/include/clang/AST/Expr.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 1679f1adff752..f6629ce066391 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -3624,7 +3624,13 @@ class MemberExpr final /// Determine whether the base of this explicit is implicit. bool isImplicitAccess() const { - return getBase() && getBase()->isImplicitCXXThis(); + if (!getBase()) return false; + + // Preserve the original C++ behavior + if (getBase()->isImplicitCXXThis()) return true; + + // Check for our new CThisExpr safely using the StmtClass enum + return getBase()->IgnoreImplicit()->getStmtClass() == Stmt::CThisExprClass; } /// Returns true if this member expression refers to a method that _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
