[clang] [LifetimeSafety] Add placement new support (PR #194030)

Utkarsh Saxena via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 07:17:35 PDT 2026


================
@@ -2898,6 +2887,219 @@ void allocate_void_ptr() {
 
 } // namespace new_allocation
 
+namespace placement_new {
+
+void placement_new_int_basic() {
+  int *p;
+  {
+    int storage;
+    p = new (&storage) int; // expected-warning {{object whose reference is captured does not live long enough}}
+  }                         // expected-note {{destroyed here}}
+  (void)*p;                 // expected-note {{later used here}}
+}
+
+void placement_new_view_from_dead_scope() {
+  View storage;
+  View *p = &storage;
+  {
+    MyObj obj;
+    p = new (&storage) View(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                               // expected-note {{destroyed here}}
+  p->use();                       // expected-note {{later used here}}
+}
+
+void placement_new_pointer_from_dead_object() {
+  MyObj *slot = nullptr;
+  MyObj **p = &slot;
+  {
+    MyObj obj;
+    p = new (&slot) MyObj *(&obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                                // expected-note {{destroyed here}}
+  (void)**p;                       // expected-note {{later used here}}
+}
+
+void placement_new_array_basic() {
+  int *p;
+  {
+    int storage[2];
+    p = new (&storage) int[2]; // expected-warning {{object whose reference is captured does not live long enough}}
+  }                            // expected-note {{destroyed here}}
+  (void)p[0];                  // expected-note {{later used here}}
+}
+
+void placement_new_array_braces() {
+  int *p;
+  {
+    int storage[2];
+    p = new (&storage) int[2]{}; // expected-warning {{object whose reference is captured does not live long enough}}
+  }                              // expected-note {{destroyed here}}
+  (void)p[0];                    // expected-note {{later used here}}
+}
+
+// FIXME: Currently `&expr` creates a brand new origin instead of reusing origins
+// from the original expression. Because of that, writes through `&expr` cannot
+// overwrite the original expression's inner storage origins. 
+// Related to https://github.com/llvm/llvm-project/issues/176291
----------------
usx95 wrote:

> Currently `&expr` creates a brand new origin instead of reusing origins from the original expression.

"reusing origins" is not the ideal solution we would move to. The problem with prematurely reusing `&expr` to underlying origin is that there is no single origin always.  For example,

```cpp
View A, B;
new (&(some_flag() ? A: B)) View(obj);​
```

Writes through the expression`some_flag() ? A: B`has two destination origins (`A` and `B`).

This likely requires an alias analysis (for origins). Let's not try to make it work by special casing it.

The only way we currently "assign" to origins is by special casing a `DeclRefExpr` on the LHS of an assignment. IIRC, there is no other "writes" to origins atm.

Let's have a single namespace for these tests added in this PR and capture that they require origin alias analysis.

---

cc: @aeft FYI as we discussed this recently. I am keen to try out a flow-insensitive origin alias analysis. Maybe that does not produce false-positives in practice and should keep things simpler to begin with.

https://github.com/llvm/llvm-project/pull/194030


More information about the cfe-commits mailing list