[clang] 1802123 - [LifetimeSafety] Propagate origins through explicit cast expressions (#192180)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 06:58:59 PDT 2026
Author: Zhijie Wang
Date: 2026-04-15T19:28:54+05:30
New Revision: 18021237534da5efd9ae6bd9f1b79243d608b58f
URL: https://github.com/llvm/llvm-project/commit/18021237534da5efd9ae6bd9f1b79243d608b58f
DIFF: https://github.com/llvm/llvm-project/commit/18021237534da5efd9ae6bd9f1b79243d608b58f.diff
LOG: [LifetimeSafety] Propagate origins through explicit cast expressions (#192180)
Before this PR, `FactsGenerator` handled cast nodes with
`VisitImplicitCastExpr` (`CastKind` switch case) and
`VisitCXXFunctionalCastExpr` (handle`gsl::Pointer` types). Other
explicit casts (`CStyleCastExpr`, `CXXStaticCastExpr`, ...) had no
handler, so origin was silently dropped. This is the root cause of
#190912: the dangle in `a = StringView(s);` is missed even though the
equivalent `StringView tmp(s); a = tmp;` is reported.
The policy for "does this cast propagate origin?" is a function of
`CastKind`, independent of whether the cast is implicit or explicit.
This PR replaces `VisitImplicitCastExpr` with a generic `VisitCastExpr`.
`VisitCXXFunctionalCastExpr` is retained only to preserve the
`handleTestPoint` logic, then delegates to `VisitCastExpr`.
This mirrors `clang/lib/AST/ExprConstant.cpp`, where each evaluator
implements only `VisitCastExpr` and switches on `CastKind`; the few
ExprClass-specific overrides (e.g., `VisitCXXDynamicCastExpr`) exist
solely to attach constexpr-validity diagnostics before delegating back.
Scope: the set of `CastKind`s that propagate origin is unchanged.
Fixes: #190912
Added:
Modified:
clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
clang/test/Sema/warn-lifetime-safety.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 3cf5971973c2a..a86f22a181a70 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -41,7 +41,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
void VisitMemberExpr(const MemberExpr *ME);
void VisitCallExpr(const CallExpr *CE);
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N);
- void VisitImplicitCastExpr(const ImplicitCastExpr *ICE);
+ void VisitCastExpr(const CastExpr *CE);
void VisitUnaryOperator(const UnaryOperator *UO);
void VisitReturnStmt(const ReturnStmt *RS);
void VisitBinaryOperator(const BinaryOperator *BO);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index fcc6035c3c1c8..5adf842e5f6d0 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -279,14 +279,14 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
getOriginsList(*N);
}
-void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
- OriginList *Dest = getOriginsList(*ICE);
+void FactsGenerator::VisitCastExpr(const CastExpr *CE) {
+ OriginList *Dest = getOriginsList(*CE);
if (!Dest)
return;
- const Expr *SubExpr = ICE->getSubExpr();
+ const Expr *SubExpr = CE->getSubExpr();
OriginList *Src = getOriginsList(*SubExpr);
- switch (ICE->getCastKind()) {
+ switch (CE->getCastKind()) {
case CK_LValueToRValue:
// TODO: Decide what to do for x-values here.
if (!SubExpr->isLValue())
@@ -297,11 +297,11 @@ void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
// `int *p, *q; p = q;`) should propagate the inner origin (what the pointer
// points to), not the outer origin (the pointer's storage location). Strip
// the outer lvalue origin.
- flow(getOriginsList(*ICE), getRValueOrigins(SubExpr, Src),
+ flow(getOriginsList(*CE), getRValueOrigins(SubExpr, Src),
/*Kill=*/true);
return;
case CK_NullToPointer:
- getOriginsList(*ICE);
+ getOriginsList(*CE);
// TODO: Flow into them a null origin.
return;
case CK_NoOp:
@@ -542,8 +542,7 @@ void FactsGenerator::VisitCXXFunctionalCastExpr(
// expression.
if (handleTestPoint(FCE))
return;
- if (isGslPointerType(FCE->getType()))
- killAndFlowOrigin(*FCE, *FCE->getSubExpr());
+ VisitCastExpr(FCE);
}
void FactsGenerator::VisitInitListExpr(const InitListExpr *ILE) {
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index c083c30f5855d..082db5d5cbf15 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -936,6 +936,33 @@ void lifetimebound_ctor() {
(void)v; // expected-note {{later used here}}
}
+void lifetimebound_ctor_functional_cast() {
+ LifetimeBoundCtor v;
+ {
+ MyObj obj;
+ v = LifetimeBoundCtor(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)v; // expected-note {{later used here}}
+}
+
+void lifetimebound_ctor_c_style_cast() {
+ LifetimeBoundCtor v;
+ {
+ MyObj obj;
+ v = (LifetimeBoundCtor)(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)v; // expected-note {{later used here}}
+}
+
+void lifetimebound_ctor_static_cast() {
+ LifetimeBoundCtor v;
+ {
+ MyObj obj;
+ v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)v; // expected-note {{later used here}}
+}
+
View lifetimebound_return_of_local() {
MyObj stack;
return Identity(stack); // expected-warning {{address of stack memory is returned later}}
More information about the cfe-commits
mailing list