[clang] [LifetimeSafety][NFC] Refactor OriginList to OriginNode tree (PR #194797)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 29 00:18:29 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
Author: Zhijie Wang (aeft)
<details>
<summary>Changes</summary>
1. Most of code is rename `OriginList` to `OriginNode`
2. Enable `OriginNode` to support more than one child
3. Field children support leave to the follow-up PR
NFC PR for #<!-- -->184344
---
Patch is 46.12 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/194797.diff
9 Files Affected:
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h (+5-6)
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+5-5)
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h (+44-30)
- (modified) clang/lib/Analysis/LifetimeSafety/Facts.cpp (+3-3)
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+122-123)
- (modified) clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp (+3-3)
- (modified) clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp (+3-3)
- (modified) clang/lib/Analysis/LifetimeSafety/Origins.cpp (+39-30)
- (modified) clang/unittests/Analysis/LifetimeSafetyTest.cpp (+3-3)
``````````diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 88b509e1b94df..15d8abfb5439d 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -20,7 +20,6 @@
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
-#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
#include <cstdint>
@@ -236,7 +235,7 @@ class GlobalEscapeFact : public OriginEscapesFact {
class UseFact : public Fact {
const Expr *UseExpr;
- const OriginList *OList;
+ const OriginNode *ONode;
// True if this use is a write operation (e.g., left-hand side of assignment).
// Write operations are exempted from use-after-free checks.
bool IsWritten = false;
@@ -244,11 +243,11 @@ class UseFact : public Fact {
public:
static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
- UseFact(const Expr *UseExpr, const OriginList *OList)
- : Fact(Kind::Use), UseExpr(UseExpr), OList(OList) {}
+ UseFact(const Expr *UseExpr, const OriginNode *ONode)
+ : Fact(Kind::Use), UseExpr(UseExpr), ONode(ONode) {}
- const OriginList *getUsedOrigins() const { return OList; }
- void setUsedOrigins(const OriginList *NewList) { OList = NewList; }
+ const OriginNode *getUsedOrigins() const { return ONode; }
+ void setUsedOrigins(const OriginNode *NewONode) { ONode = NewONode; }
const Expr *getUseExpr() const { return UseExpr; }
void markAsWritten() { IsWritten = true; }
bool isWritten() const { return IsWritten; }
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 45b16cf1ec31d..b5185ba815b00 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -57,13 +57,13 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
void VisitCXXDeleteExpr(const CXXDeleteExpr *DE);
private:
- OriginList *getOriginsList(const ValueDecl &D);
- OriginList *getOriginsList(const Expr &E);
+ OriginNode *getOriginNode(const ValueDecl &D);
+ OriginNode *getOriginNode(const Expr &E);
bool hasOrigins(QualType QT) const;
bool hasOrigins(const Expr *E) const;
- void flow(OriginList *Dst, OriginList *Src, bool Kill);
+ void flow(OriginNode *Dst, OriginNode *Src, bool Kill);
void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
@@ -106,12 +106,12 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
template <typename Destination, typename Source>
void flowOrigin(const Destination &D, const Source &S) {
- flow(getOriginsList(D), getOriginsList(S), /*Kill=*/false);
+ flow(getOriginNode(D), getOriginNode(S), /*Kill=*/false);
}
template <typename Destination, typename Source>
void killAndFlowOrigin(const Destination &D, const Source &S) {
- flow(getOriginsList(D), getOriginsList(S), /*Kill=*/true);
+ flow(getOriginNode(D), getOriginNode(S), /*Kill=*/true);
}
/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index c2db59c579060..f2d94e99d6ac2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -36,7 +36,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
///
/// Each Origin corresponds to a single level of indirection. For complex types
/// with multiple levels of indirection (e.g., `int**`), multiple Origins are
-/// organized into an OriginList structure (see below).
+/// organized into a tree structure (see below).
struct Origin {
OriginID ID;
/// A pointer to the AST node that this origin represents. This union
@@ -92,30 +92,42 @@ struct Origin {
///
/// The list structure enables the analysis to track how loans flow through
/// different levels of indirection when assignments and dereferences occur.
-class OriginList {
+///
+/// TODO: Currently list-shaped (each node has at most one pointee child).
+/// Will become tree-shaped once field children are added to support
+/// origin trees for records whose fields have origins.
+class OriginNode {
public:
- OriginList(OriginID OID) : OuterOID(OID) {}
+ OriginNode(OriginID OID) : OID(OID) {}
- OriginList *peelOuterOrigin() const { return InnerList; }
- OriginID getOuterOriginID() const { return OuterOID; }
+ OriginNode *getPointeeChild() const {
+ return NumChildren ? Children[0] : nullptr;
+ }
- void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
+ OriginID getOriginID() const { return OID; }
- // Used for assertion checks only (to ensure origin lists have matching
+ void setChildren(OriginNode **Arr, unsigned N) {
+ assert(Children == nullptr && "children must be set at most once");
+ Children = Arr;
+ NumChildren = N;
+ }
+
+ // Used for assertion checks only (to ensure pointee chains have matching
// lengths).
size_t getLength() const {
size_t Length = 1;
- const OriginList *T = this;
- while (T->InnerList) {
- T = T->InnerList;
+ const OriginNode *T = this;
+ while (auto *ON = T->getPointeeChild()) {
+ T = ON;
Length++;
}
return Length;
}
private:
- OriginID OuterOID;
- OriginList *InnerList = nullptr;
+ OriginID OID;
+ OriginNode **Children = nullptr;
+ unsigned NumChildren = 0;
};
bool doesDeclHaveStorage(const ValueDecl *D);
@@ -126,31 +138,31 @@ class OriginManager {
public:
explicit OriginManager(const AnalysisDeclContext &AC);
- /// Gets or creates the OriginList for a given ValueDecl.
+ /// Gets or creates the OriginNode for a given ValueDecl.
///
/// Creates a list structure mirroring the levels of indirection in the
/// declaration's type (e.g., `int** p` creates list of size 2).
///
- /// \returns The OriginList, or nullptr if the type is not pointer-like.
- OriginList *getOrCreateList(const ValueDecl *D);
+ /// \returns The OriginNode, or nullptr if the type is not pointer-like.
+ OriginNode *getOrCreateNode(const ValueDecl *D);
- /// Gets or creates the OriginList for a given Expr.
+ /// Gets or creates the OriginNode for a given Expr.
///
/// Creates a list based on the expression's type and value category:
/// - Lvalues get an implicit reference level (modeling addressability)
/// - Rvalues of non-pointer type return nullptr (no trackable origin)
/// - DeclRefExpr may reuse the underlying declaration's list
///
- /// \returns The OriginList, or nullptr for non-pointer rvalues.
- OriginList *getOrCreateList(const Expr *E);
+ /// \returns The OriginNode, or nullptr for non-pointer rvalues.
+ OriginNode *getOrCreateNode(const Expr *E);
- /// Wraps an existing OriginID in a new single-element OriginList, so a fact
- /// can refer to a single level of an existing OriginList.
- OriginList *createSingleOriginList(OriginID OID);
+ /// Wraps an existing OriginID in a new single-element OriginNode, so a fact
+ /// can refer to a single level of an existing OriginNode.
+ OriginNode *createSingleOriginNode(OriginID OID);
- /// Returns the OriginList for the implicit 'this' parameter if the current
+ /// Returns the OriginNode for the implicit 'this' parameter if the current
/// declaration is an instance method.
- std::optional<OriginList *> getThisOrigins() const { return ThisOrigins; }
+ std::optional<OriginNode *> getThisOrigins() const { return ThisOrigins; }
const Origin &getOrigin(OriginID ID) const;
@@ -169,11 +181,13 @@ class OriginManager {
private:
OriginID getNextOriginID() { return NextOriginID++; }
- OriginList *createNode(const ValueDecl *D, QualType QT);
- OriginList *createNode(const Expr *E, QualType QT);
+ OriginNode *createNode(const ValueDecl *D, QualType QT);
+ OriginNode *createNode(const Expr *E, QualType QT);
+
+ void attachPointeeChild(OriginNode *Parent, OriginNode *Pointee);
template <typename T>
- OriginList *buildListForType(QualType QT, const T *Node);
+ OriginNode *buildNodeForType(QualType QT, const T *Node);
void initializeThisOrigins(const Decl *D);
@@ -188,10 +202,10 @@ class OriginManager {
/// TODO(opt): Profile and evaluate the usefulness of small buffer
/// optimisation.
llvm::SmallVector<Origin> AllOrigins;
- llvm::BumpPtrAllocator ListAllocator;
- llvm::DenseMap<const clang::ValueDecl *, OriginList *> DeclToList;
- llvm::DenseMap<const clang::Expr *, OriginList *> ExprToList;
- std::optional<OriginList *> ThisOrigins;
+ llvm::BumpPtrAllocator Allocator;
+ llvm::DenseMap<const clang::ValueDecl *, OriginNode *> DeclToNode;
+ llvm::DenseMap<const clang::Expr *, OriginNode *> ExprToNode;
+ std::optional<OriginNode *> ThisOrigins;
/// Types that are not inherently pointer-like but require origin tracking
/// because of lifetime annotations (currently [[clang::lifetimebound]]) on
/// functions that return them.
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..d04d3e9202952 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -82,9 +82,9 @@ void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &,
OS << "Use (";
size_t NumUsedOrigins = getUsedOrigins()->getLength();
size_t I = 0;
- for (const OriginList *Cur = getUsedOrigins(); Cur;
- Cur = Cur->peelOuterOrigin(), ++I) {
- OM.dump(Cur->getOuterOriginID(), OS);
+ for (const OriginNode *Cur = getUsedOrigins(); Cur;
+ Cur = Cur->getPointeeChild(), ++I) {
+ OM.dump(Cur->getOriginID(), OS);
if (I < NumUsedOrigins - 1)
OS << ", ";
}
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index efdb1a1691ae3..24bf706b62ee3 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -30,11 +30,12 @@
namespace clang::lifetimes::internal {
using llvm::isa_and_present;
-OriginList *FactsGenerator::getOriginsList(const ValueDecl &D) {
- return FactMgr.getOriginMgr().getOrCreateList(&D);
+OriginNode *FactsGenerator::getOriginNode(const ValueDecl &D) {
+ return FactMgr.getOriginMgr().getOrCreateNode(&D);
}
-OriginList *FactsGenerator::getOriginsList(const Expr &E) {
- return FactMgr.getOriginMgr().getOrCreateList(&E);
+
+OriginNode *FactsGenerator::getOriginNode(const Expr &E) {
+ return FactMgr.getOriginMgr().getOrCreateNode(&E);
}
bool FactsGenerator::hasOrigins(QualType QT) const {
@@ -58,7 +59,7 @@ bool FactsGenerator::hasOrigins(const Expr *E) const {
/// * Level 1: pp <- p's address
/// * Level 2: (*pp) <- what p points to (i.e., &x)
/// - `View v = obj;` flows origins from `obj` (depth 1) to `v` (depth 1)
-void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill) {
+void FactsGenerator::flow(OriginNode *Dst, OriginNode *Src, bool Kill) {
if (!Dst)
return;
assert(Src &&
@@ -68,9 +69,9 @@ void FactsGenerator::flow(OriginList *Dst, OriginList *Src, bool Kill) {
while (Dst && Src) {
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
- Dst->getOuterOriginID(), Src->getOuterOriginID(), Kill));
- Dst = Dst->peelOuterOrigin();
- Src = Src->peelOuterOrigin();
+ Dst->getOriginID(), Src->getOriginID(), Kill));
+ Dst = Dst->getPointeeChild();
+ Src = Src->getPointeeChild();
}
}
@@ -146,22 +147,22 @@ void FactsGenerator::run() {
///
/// Example: For `View& v`, returns the origin of what v points to, not v's
/// storage.
-static OriginList *getRValueOrigins(const Expr *E, OriginList *List) {
- if (!List)
+static OriginNode *getRValueOrigins(const Expr *E, OriginNode *Node) {
+ if (!Node)
return nullptr;
- return E->isGLValue() ? List->peelOuterOrigin() : List;
+ return E->isGLValue() ? Node->getPointeeChild() : Node;
}
void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
for (const Decl *D : DS->decls())
if (const auto *VD = dyn_cast<VarDecl>(D))
if (const Expr *InitExpr = VD->getInit()) {
- OriginList *VDList = getOriginsList(*VD);
- if (!VDList)
+ OriginNode *VDNode = getOriginNode(*VD);
+ if (!VDNode)
continue;
- OriginList *InitList = getOriginsList(*InitExpr);
- assert(InitList && "VarDecl had origins but InitExpr did not");
- flow(VDList, InitList, /*Kill=*/true);
+ OriginNode *InitNode = getOriginNode(*InitExpr);
+ assert(InitNode && "VarDecl had origins but InitExpr did not");
+ flow(VDNode, InitNode, /*Kill=*/true);
}
}
@@ -182,13 +183,13 @@ void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
if (doesDeclHaveStorage(DRE->getDecl())) {
const Loan *L = createLoan(FactMgr, DRE);
assert(L);
- OriginList *List = getOriginsList(*DRE);
- assert(List &&
+ OriginNode *Node = getOriginNode(*DRE);
+ assert(Node &&
"gl-value DRE of non-pointer type should have an origin list");
// This loan specifically tracks borrowing the variable's storage location
- // itself and is issued to outermost origin (List->OID).
+ // itself and is issued to outermost origin (Node->OID).
CurrentBlockFacts.push_back(
- FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
+ FactMgr.createFact<IssueFact>(L->getID(), Node->getOriginID()));
}
}
@@ -204,8 +205,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
CCE->getConstructor()->isDefaulted() && CCE->getNumArgs() == 1 &&
hasOrigins(CCE->getType())) {
const Expr *Arg = CCE->getArg(0);
- if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) {
- flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
+ if (OriginNode *ArgNode = getRValueOrigins(Arg, getOriginNode(*Arg))) {
+ flow(getOriginNode(*CCE), ArgNode, /*Kill=*/true);
return;
}
}
@@ -214,8 +215,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
RD && isStdCallableWrapperType(RD) && CCE->getNumArgs() == 1) {
const Expr *Arg = CCE->getArg(0);
- if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) {
- flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
+ if (OriginNode *ArgNode = getRValueOrigins(Arg, getOriginNode(*Arg))) {
+ flow(getOriginNode(*CCE), ArgNode, /*Kill=*/true);
return;
}
}
@@ -263,14 +264,14 @@ void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
auto *MD = ME->getMemberDecl();
if (isa<FieldDecl>(MD) && doesDeclHaveStorage(MD)) {
assert(ME->isGLValue() && "Field member should be GL value");
- OriginList *Dst = getOriginsList(*ME);
+ OriginNode *Dst = getOriginNode(*ME);
assert(Dst && "Field member should have an origin list as it is GL value");
- OriginList *Src = getOriginsList(*ME->getBase());
+ OriginNode *Src = getOriginNode(*ME->getBase());
assert(Src && "Base expression should be a pointer/reference type");
// The field's glvalue (outermost origin) holds the same loans as the base
// expression.
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
- Dst->getOuterOriginID(), Src->getOuterOriginID(),
+ Dst->getOriginID(), Src->getOriginID(),
/*Kill=*/true));
}
}
@@ -284,15 +285,15 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
const CXXNullPtrLiteralExpr *N) {
/// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
/// pointers can use the same type of loan.
- getOriginsList(*N);
+ getOriginNode(*N);
}
void FactsGenerator::VisitCastExpr(const CastExpr *CE) {
- OriginList *Dest = getOriginsList(*CE);
+ OriginNode *Dest = getOriginNode(*CE);
if (!Dest)
return;
const Expr *SubExpr = CE->getSubExpr();
- OriginList *Src = getOriginsList(*SubExpr);
+ OriginNode *Src = getOriginNode(*SubExpr);
switch (CE->getCastKind()) {
case CK_LValueToRValue:
@@ -304,11 +305,11 @@ void FactsGenerator::VisitCastExpr(const CastExpr *CE) {
// `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(*CE), getRValueOrigins(SubExpr, Src),
+ flow(getOriginNode(*CE), getRValueOrigins(SubExpr, Src),
/*Kill=*/true);
return;
case CK_NullToPointer:
- getOriginsList(*CE);
+ getOriginNode(*CE);
// TODO: Flow into them a null origin.
return;
case CK_NoOp:
@@ -326,7 +327,7 @@ void FactsGenerator::VisitCastExpr(const CastExpr *CE) {
case CK_ArrayToPointerDecay:
assert(Src && "Array expression should have origins as it is GL value");
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
- Dest->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true));
+ Dest->getOriginID(), Src->getOriginID(), /*Kill=*/true));
return;
case CK_FunctionToPointerDecay:
case CK_BuiltinFnToFnPtr:
@@ -360,38 +361,38 @@ void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) {
void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) {
if (const Expr *RetExpr = RS->getRetValue()) {
- if (OriginList *List = getOriginsList(*RetExpr))
- for (OriginList *L = List; L != nullptr; L = L->peelOuterOrigin())
- EscapesInCurrentBlock.push_back(FactMgr.createFact<ReturnEscapeFact>(
- L->getOuterOriginID(), RetExpr));
+ if (OriginNode *Node = getOriginNode(*RetExpr))
+ for (OriginNode *L = Node; L != nullptr; L = L->getPointeeChild())
+ EscapesInCurrentBlock.push_back(
+ FactMgr.createFact<ReturnEscapeFact>(L->getOriginID(), RetExpr));
}
}
void FactsGenerator::handleAssignment(const Expr *LHSExpr,
const Expr *RHSExpr) {
LHSExpr = LHSExpr->IgnoreParenImpCasts();
- OriginList *LHSList = nullptr;
+ OriginNode *LHSNode = nullptr;
if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) {
- LHSList = getOriginsList(*DRE_LHS);
- assert(LHSList && "LHS is a DRE and should have an origin list");
+ LHSNode = getOriginNode(*DRE_LHS);
+ assert(LHSNode && "LHS is a DRE and should have an origin list");
}
// Handle assignment to member fields (e.g., `this->view = s` or `view = s`).
// This enables detection of dangling fields when local values escape to
// fields.
if (const auto *ME_LHS = dyn_cast<MemberExpr>(LHSExpr)) {
- LHSList = getOriginsList(*ME_LHS);
- assert(LHSList && "LHS is a MemberExpr and should have an origin list");
+ LHSNode = getOriginNode(*ME_LHS);
+ assert(LHSNode && "LHS is a MemberExpr and should have an origin list");
}
- if (!LHSList)
+ if (!LHSNode)
return;
- OriginList *RHSList = getOriginsList(*RHSExpr);
+ OriginNode *RHSNode = getOriginNode(*RHSExpr);
// For operator= with reference parameters (e.g.,
// `View& operator=(const View&)`), the RHS argument stays an lvalue,
// unlike built-in assignment where LValueToRValue cast strips the outer
// lvalue origin. Strip it manually to get the actual value origins being
// assigned.
- RHSList = getRValueOrigins(RHSExpr, RHSList);
+ RHSNode = getRValueOrigins(RHSExpr, RHSNode);
if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) {
QualType QT = DRE_LHS->getDecl()->getType();
@@ -402,12 +403,12 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr,
// binding live) and a Write of the inner origins (killing the pointee's
// liveness).
if (UseFact *UF = UseFacts.lookup(DRE_LHS)) {
- const OriginList *FullList = UF->getUsedOrigins();
- assert(FullList);
- UF->setUsedOrigins(FactMgr.getOriginMgr().createSingleOriginList(
- FullLi...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/194797
More information about the cfe-commits
mailing list