[clang] [LifetimeSafety] Add placement new support (PR #194030)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 28 06:37:46 PDT 2026
================
@@ -2898,6 +2887,210 @@ 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
+struct PlacementNewInMethod {
+ View V;
+
+ void bad_store_after_placement_new() {
+ {
+ MyObj obj;
+ new (&V) View(obj);
+ }
+ V.use();
+ }
+};
+
+void placement_new_member_call_from_dead_scope() {
+ View *storage = new View;
+ {
+ MyObj obj;
+ new (storage) View(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ storage->use(); // expected-note {{later used here}}
+}
+
+struct ViewPointerFieldHolder {
+ View *Ptr;
+};
+
+// FIXME: Repeated field accesses do not share stable field origins.
+void placement_new_pointer_field_from_dead_scope() {
+ ViewPointerFieldHolder h{new View};
+ {
+ MyObj obj;
+ new (h.Ptr) View(obj);
+ }
+ h.Ptr->use();
+}
+
+// FIXME: Repeated array element accesses do not share stable element origins.
+void placement_new_array_subscript_from_dead_scope() {
+ View *slots[1] = {new View};
+ {
+ MyObj obj;
+ new (slots[0]) View(obj);
+ }
+ slots[0]->use();
+}
+
+// FIXME: Writes through references do not update the referred pointer's origins.
+void placement_new_pointer_reference_from_dead_scope() {
+ View *storage = new View;
+ View *&ref = storage;
+ {
+ MyObj obj;
+ new (ref) View(obj);
+ }
+ storage->use();
+}
+
+// FIXME: Writing through a conditional glvalue is not propagated to either arm.
+void placement_new_conditional_pointer_from_dead_scope(bool flag) {
+ View *x = new View;
+ View *y = new View;
+ {
+ MyObj obj;
+ new (flag ? x : y) View(obj);
+ }
+ x->use();
+ y->use();
+}
+
+View *identity(View *p [[clang::lifetimebound]]);
+
+// FIXME: Function call results are not mapped back to the argument they expose.
+void placement_new_function_pointer_from_dead_scope() {
+ View *storage = new View;
+ {
+ MyObj obj;
+ new (identity(storage)) View(obj);
+ }
+ storage->use();
+}
+
+struct ViewStorage {
+ View Storage;
+ View *get() [[clang::lifetimebound]];
+};
+
+// FIXME: Placement-new does not write back through the lifetimebound result of `get()`.
+void placement_new_member_function_pointer_from_dead_scope() {
+ ViewStorage storage;
+ {
+ MyObj obj;
+ new (storage.get()) View(obj);
+ }
+ storage.Storage.use();
+}
+
+// FIXME: Address-of placement arguments are not resolved to the addressed object.
+void placement_new_addressof_from_dead_scope() {
+ View storage;
+ {
+ MyObj obj;
+ new (&storage) View(obj);
+ }
+ storage.use();
+}
+
+// FIXME: Explicit casts to `void *` are not ignored for placement arguments.
+void placement_new_explicit_void_cast_from_dead_scope() {
+ View *storage = new View;
+ {
+ MyObj obj;
+ new ((void *)storage) View(obj);
+ }
+ storage->use();
+}
----------------
NeKon69 wrote:
This can be fixed, although I am not sure if it is worth to, if it is, please let me know.
https://github.com/llvm/llvm-project/pull/194030
More information about the cfe-commits
mailing list