[clang] [BoundsSafety][Clang][Sema][AST] Introduce CThisExpr for C __counted_by attributes (PR #199241)
Karthik P via cfe-commits
cfe-commits at lists.llvm.org
Fri May 22 21:25:21 PDT 2026
https://github.com/Caryoake updated https://github.com/llvm/llvm-project/pull/199241
>From 0230ce5c8d2eb7a558cb8255f8a28659ac73df01 Mon Sep 17 00:00:00 2001
From: Karthik <prakashkarthik751 at gmail.com>
Date: Fri, 22 May 2026 21:23:33 -0700
Subject: [PATCH] [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 393fe275c62692..aa0811cbe19f42 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 b5be0910194bd4..94058e99c9ce8d 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 e166894ea024b0..1eb9a7b6faf01e 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 eb452f5da67874..0cc223987e6622 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 6c3294573e9d4c..d569d8637f3fb0 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 eb25e5260fd1a3..d1abd6d75509ba 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 55b6cbcbba57d9..1e5b35346234c7 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 44ac4f6630690b..6475c8555b4171 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 444795c3b67b90..88c767421faa44 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 4ada1dc58042d0..61af99b9dbc41b 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 47cbef06c3cc44..9a4152da3903bb 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 00000000000000..6a4c6e4812c906
--- /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 *'
More information about the cfe-commits
mailing list