================
@@ -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