llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis

@llvm/pr-subscribers-clang-temporal-safety

Author: Utkarsh Saxena (usx95)

<details>
<summary>Changes</summary>

### **Seeking early inputs on invalidation annotation.**

**Background:**

The lifetime safety analysis currently detects invalidations for standard 
containers (e.g., `std::vector::push_back` invalidates references). However, we 
lack a way to extend this capability to user-defined types.

Today, we can express "alias" contracts using `[[clang::lifetimebound]]` and 
`[[clang::lifetime_capture_by(...)]]`, which describe how return values or 
captured references relate to function parameters. What we're missing is a way 
to express "invalidation" contracts: marking when a function call invalidates 
the object or its references.

**Proposal for** **`[[clang::invalidates]]`:**

This PR proposes `[[clang::annotate_type("experimental_invalidates")]]` 
(prototype for `[[clang::invalidates]]`) to mark member functions that 
invalidate `this`. This allows user-defined container types to participate in 
lifetime safety analysis the same way standard containers do.

**Semantics:**

The annotation can be applied to function types, marking either the implicit 
`this` parameter (for member functions) or explicit function parameters.

When applied, it expresses an invalidation contract: calling the annotated 
function performs "interior destruction" on the marked object. The object 
itself remains valid, but pointers and references pointing to its inner memory 
regions are invalidated. The analysis enforces that such invalidated 
pointers/references are not used after the invalidating call.

Example usage:

```cpp
struct FlatContainer {
  void push_back(int x) [[clang::annotate_type("experimental_invalidates")]] {
    data.push_back(x);
  }
  int&amp; getFirst() [[clang::lifetimebound]] { return data[0]; }
private:
  std::vector&lt;int&gt; data;
};

void foo() {
  FlatContainer C;
  C.push_back(2);
  int&amp; first = C.getFirst();
  C.push_back(2); // warning: invalidates 'first'
  (void)first;    // use after invalidation
}

```

**Considerations in this proposal:**

The attribute can be placed on function parameters and the `this` parameter. 
Currently, this PR only handles the `this` argument. The analysis can be 
extended to mark non-const reference type parameters as being invalidated by 
the function call as well.

Example for non-const reference parameter invalidation:

```cpp
void resize(Container&amp; c [[clang::invalidates]], size_t new_size) {
  // This function invalidates references into 'c'
  c.clear();
  c.reserve(new_size);
}

```

**Limitations:**

As with the family of existing lifetime annotations, this annotation is not 
sufficient to express granular invalidations. For example, a struct where a 
function invalidates one field but not the other:

```cpp
struct S {
  void insertA(int x) { a.push_back(x); } // invalidates 'a' but does not 
invalidate 'b'
  void insertB(int x) { b.push_back(x); } // vice versa!
  std::vector&lt;int&gt; a, b;
};

```

---

**Experimental Purpose:**

This is strictly experimental, seeking initial feedback before starting an RFC 
on Discourse. We may land this as-is to evaluate its usefulness and 
expressiveness, but it's not available for wider adoption until a proper named 
annotation is introduced via RFC.



---
Full diff: https://github.com/llvm/llvm-project/pull/186799.diff


2 Files Affected:

- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp (+29) 
- (modified) clang/test/Sema/warn-lifetime-safety-invalidations.cpp (+21) 


``````````diff
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 4852f444a51b3..0555d1663d515 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -277,7 +277,36 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD) {
          MD.getNumParams() == 0 && isStdUniquePtr(*MD.getParent());
 }
 
+static bool hasInvalidatesAttr(const TypeSourceInfo &TSI) {
+  // Walk through the type layers looking for an annotate attribute.
+  TypeLoc TL = TSI.getTypeLoc();
+  while (true) {
+    auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
+    if (!ATL)
+      break;
+    if (auto *AnnAttr = ATL.getAttrAs<AnnotateTypeAttr>()) {
+      if (AnnAttr->getAnnotation() == "experimental_invalidates")
+        return true;
+    }
+    TL = ATL.getModifiedLoc();
+  }
+  return false;
+}
+
 bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
+  auto CheckRedecls = [](const FunctionDecl *F) {
+    return llvm::any_of(F->redecls(), [](const FunctionDecl *Redecl) {
+      const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo();
+      return TSI && hasInvalidatesAttr(*TSI);
+    });
+  };
+
+  if (CheckRedecls(&MD))
+    return true;
+  if (const FunctionDecl *Pattern = MD.getTemplateInstantiationPattern();
+      Pattern && CheckRedecls(Pattern))
+    return true;
+
   const CXXRecordDecl *RD = MD.getParent();
   if (!isInStlNamespace(RD))
     return false;
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp 
b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index 486edd7a1a023..cda35bb62f5c4 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -507,3 +507,24 @@ void ref_capture_reassigned_to_safe() {
   lambda();  // should not warn
 }
 } // namespace lambda_capture_invalidation
+
+namespace invalidation_annotation {
+struct FlatContainer {
+  void push_back(int x) [[clang::annotate_type("experimental_invalidates")]] {
+    data.push_back(x);
+  }
+
+  int& getFirst() [[clang::lifetimebound]] { return data[0]; }
+
+ private:
+  std::vector<int> data;
+};
+
+void foo() {
+  FlatContainer C;
+  C.push_back(1);
+  int& first = C.getFirst();  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  C.push_back(2);             // expected-note {{invalidated here}}
+  (void)first;                // expected-note {{later used here}}
+}
+} // namespace invalidation_annotation

``````````

</details>


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

Reply via email to