================
@@ -7,85 +7,152 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 
 namespace clang::lifetimes::internal {
 
-void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
-  OS << OID << " (";
-  Origin O = getOrigin(OID);
-  if (const ValueDecl *VD = O.getDecl())
-    OS << "Decl: " << VD->getNameAsString();
-  else if (const Expr *E = O.getExpr())
-    OS << "Expr: " << E->getStmtClassName();
-  else
-    OS << "Unknown";
-  OS << ")";
+bool hasOrigins(QualType QT) {
+  return QT->isPointerOrReferenceType() || isGslPointerType(QT);
 }
 
-Origin &OriginManager::addOrigin(OriginID ID, const clang::ValueDecl &D) {
-  AllOrigins.emplace_back(ID, &D);
-  return AllOrigins.back();
+/// Determines if an expression has origins that need to be tracked.
+///
+/// An expression has origins if:
+/// - It's a glvalue (has addressable storage), OR
+/// - Its type is pointer-like (pointer, reference, or gsl::Pointer)
+///
+/// Examples:
+/// - `int x; x` : has origin (glvalue)
+/// - `int* p; p` : has 2 origins (1 for glvalue and 1 for pointer type)
+/// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
+/// - `42` : no origin (prvalue of non-pointer type)
+/// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
+bool hasOrigins(const Expr *E) {
+  return E->isGLValue() || hasOrigins(E->getType());
 }
 
-Origin &OriginManager::addOrigin(OriginID ID, const clang::Expr &E) {
-  AllOrigins.emplace_back(ID, &E);
-  return AllOrigins.back();
+/// Returns true if the declaration has its own storage that can be borrowed.
+///
+/// References generally have no storage - they are aliases to other storage.
+/// For example:
+///   int x;      // has storage (can issue loans to x's storage)
+///   int& r = x; // no storage (r is an alias to x's storage)
+///   int* p;     // has storage (the pointer variable p itself has storage)
+///
+/// TODO: Handle lifetime extension. References initialized by temporaries
+/// can have storage when the temporary's lifetime is extended:
+///   const int& r = 42; // temporary has storage, lifetime extended
+///   Foo&& f = Foo{};   // temporary has storage, lifetime extended
+/// Currently, this function returns false for all reference types.
+bool doesDeclHaveStorage(const ValueDecl *D) {
+  return !D->getType()->isReferenceType();
 }
 
-// TODO: Mark this method as const once we remove the call to getOrCreate.
-OriginID OriginManager::get(const Expr &E) {
-  if (auto *ParenIgnored = E.IgnoreParens(); ParenIgnored != &E)
-    return get(*ParenIgnored);
-  auto It = ExprToOriginID.find(&E);
-  if (It != ExprToOriginID.end())
-    return It->second;
-  // If the expression itself has no specific origin, and it's a reference
-  // to a declaration, its origin is that of the declaration it refers to.
-  // For pointer types, where we don't pre-emptively create an origin for the
-  // DeclRefExpr itself.
-  if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
-    return get(*DRE->getDecl());
-  // TODO: This should be an assert(It != ExprToOriginID.end()). The current
-  // implementation falls back to getOrCreate to avoid crashing on
-  // yet-unhandled pointer expressions, creating an empty origin for them.
-  return getOrCreate(E);
+OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) {
+  OriginID NewID = getNextOriginID();
+  AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
+  return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
 }
 
-OriginID OriginManager::get(const ValueDecl &D) {
-  auto It = DeclToOriginID.find(&D);
-  // TODO: This should be an assert(It != DeclToOriginID.end()). The current
-  // implementation falls back to getOrCreate to avoid crashing on
-  // yet-unhandled pointer expressions, creating an empty origin for them.
-  if (It == DeclToOriginID.end())
-    return getOrCreate(D);
+OriginList *OriginManager::createNode(const Expr *E, QualType QT) {
+  OriginID NewID = getNextOriginID();
+  AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
+  return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
+}
 
-  return It->second;
+template <typename T>
+OriginList *OriginManager::buildListForType(QualType QT, const T *Node) {
+  assert(hasOrigins(QT) && "buildListForType called for non-pointer type");
+  OriginList *Root = createNode(Node, QT);
+  if (QT->isPointerOrReferenceType()) {
+    QualType PointeeTy = QT->getPointeeType();
+    // We recurse if the pointee type is pointer-like, to build the next
+    // level in the origin tree. E.g., for T*& / View&.
+    if (hasOrigins(PointeeTy))
+      Root->setInnerOriginList(buildListForType(PointeeTy, Node));
----------------
usx95 wrote:

I would prefer to keep this recursive for readability and would expect the 
depth to be quite limited

https://github.com/llvm/llvm-project/pull/168344
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to