================
@@ -4318,6 +4319,324 @@ void foo() {
                             // expected-note {{result of call to 'get' aliases 
the storage of local variable 'o' because the implicit object parameter is 
inferred as lifetimebound}}
         up = std::move(o);  // expected-note {{potentially moved here}}
     }                       // expected-note {{local variable 'o' is destroyed 
here}}
-    (void)*p;               // expected-note {{later used here}}
+    use(*p);                // expected-note {{later used here}}
 }
 } // namespace TakeOwnershipTests
+
+//===----------------------------------------------------------------------===//
+// What counts as a use
+//
+// A use is an access through an lvalue: reading it (an lvalue-to-rvalue
+// conversion) or writing through it. The loans of the accessed lvalue say 
which
+// objects it may name, so a dereference needs no special handling. Taking an
+// address, naming a variable, or copying a pointer out of one is not an 
access.
+//===----------------------------------------------------------------------===//
+
+namespace std { class type_info; }
+
+namespace what_is_a_use {
+struct Node {
+  int id;
+  Node *next;
+};
+
+void copying_a_pointer_is_not_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;  // expected-warning {{local variable 'local' does not live 
long enough}}
+  }              // expected-note {{local variable 'local' is destroyed here}}
+  Node *q = p;   // Reads p, not *p.
+                 // expected-note@-1 {{local variable 'p' aliases the storage 
of local variable 'local'}}
+  use(q);        // expected-note {{later used here}}
+}
+
+void taking_an_address_is_not_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;
+  }
+  Node **pp = &p;          // no-warning: borrows p's storage, never reads it.
+  Node *reborrow = &*p;    // no-warning: reborrows, no access.
+  Node **pnext = &p->next; // no-warning: address of a field.
+  (void)pp; (void)reborrow; (void)pnext;
+}
+
+void reading_through_a_pointer_is_a_use() {
+  Node *p;
+  int sink;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  sink = p->id;   // expected-note {{later used here}}
+  (void)sink;
+}
+
+// Loading a scalar is not a use of the scalar; it is a use of the pointer that
+// was dereferenced to reach it.
+void reading_a_pointer_field_is_a_use() {
+  Node *p;
+  Node *sink;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  sink = p->next; // expected-note {{later used here}}
+  (void)sink;
+}
+
+void writing_through_a_pointer_is_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;      // expected-warning {{local variable 'local' does not 
live long enough}}
+  }                  // expected-note {{local variable 'local' is destroyed 
here}}
+  p->id = 1;         // expected-note {{later used here}}
+  p->next = nullptr;
+}
+
+void incrementing_through_a_pointer_is_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  p->id++;        // expected-note {{later used here}}
+}
+
+// Incrementing the pointer itself only touches p's own storage.
+void incrementing_the_pointer_is_not_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;
+  }
+  p++;             // no-warning
+  p += 1;          // no-warning
+}
+
+void discarding_the_value_is_not_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;
+  }
+  (void)p;         // no-warning
+}
+
+void element_access(int i) {
+  Node *arr[4];
+  Node *p, *sink;
+  {
+    Node local;
+    p = &local;
+  }
+  Node **elem = &arr[i];  // no-warning: address of an element.
+  arr[i] = p;             // Reads p, writes the element; neither reads *p.
+  sink = arr[i];          // Reads the element, which names part of arr.
+  (void)elem; (void)sink;
+}
+
+// A dereference only accesses the level actually loaded.
+void one_level_per_load() {
+  Node **pp;
+  {
+    Node *inner;
+    Node outer;
+    inner = &outer;
+    pp = &inner;    // expected-warning {{local variable 'inner' does not live 
long enough}}
+  }                 // expected-note {{local variable 'inner' is destroyed 
here}}
+  Node *q = *pp;    // expected-note {{later used here}}
+  (void)q;          // Reads pp, so it names 'inner'; 'outer' is never read.
+}
+
+void reading_through_a_reference_is_a_use() {
+  Node *p;
+  int sink;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  Node &r = *p;   // Binding a reference is a reborrow, not an access.
+                  // expected-note@-1 {{local variable 'p' aliases the storage 
of local variable 'local'}}
+  sink = r.id;    // expected-note {{later used here}}
+  (void)sink;
+}
+
+void writing_through_a_reference_is_a_use() {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  Node &r = *p;   // expected-note {{local variable 'p' aliases the storage of 
local variable 'local'}}
+  r.id = 1;       // expected-note {{later used here}}
+}
+
+void through_a_conditional(bool cond) {
+  Node *p1, *p2, *reborrow;
+  int sink;
+  {
+    Node a, b;
+    p1 = &a;        // expected-warning {{local variable 'a' does not live 
long enough}}
+    p2 = &b;        // expected-warning {{local variable 'b' does not live 
long enough}}
+  }                 // expected-note 2 {{destroyed here}}
+  reborrow = &*(cond ? p1 : p2);   // no-warning: reborrow only.
+  sink = (cond ? p1 : p2)->id;     // expected-note 2 {{later used here}}
+  (cond ? p1 : p2)->id = 1;
+  (void)reborrow; (void)sink;
+}
+
+// Opaque code may dereference what it is handed, so every argument is a use --
+// including when there is no FunctionDecl to inspect.
+namespace opaque_callees {
+void (*g_fp)(Node *);
+struct Callable { void m(Node *); };
+
+void through_function_pointer(void (*fp)(Node *)) {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  fp(p);          // expected-note {{later used here}}
+}
+
+void through_global_function_pointer() {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  g_fp(p);        // expected-note {{later used here}}
+}
+
+void through_pointer_to_member(Callable &c, void (Callable::*pmf)(Node *)) {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  (c.*pmf)(p);    // expected-note {{later used here}}
+}
+
+// A view has no lvalue-to-rvalue conversion of its own, so the argument rule 
is
+// the only thing covering it.
+void view_through_function_pointer(void (*fp)(View)) {
+  View v;
+  {
+    MyObj local;
+    v = local;    // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  fp(v);          // expected-note {{later used here}}
+}
+
+#ifdef __cpp_exceptions
+void through_throw() {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  throw p;        // expected-note {{later used here}}
+}
+#endif
+
+void through_inline_asm() {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  asm volatile("" :: "r"(p)); // expected-note {{later used here}}
+}
+
+void through_placement_new() {
+  Node *p;
+  {
+    Node local;
+    p = &local;          // expected-warning {{local variable 'local' does not 
live long enough}}
+  }                      // expected-note {{local variable 'local' is 
destroyed here}}
+  new (p) Node;          // expected-note {{later used here}}
+}
+
+// Reading the dangling value and then overwriting it is still a use; the read
+// happens first.
+Node *ident(Node *);
+void read_then_overwrite() {
+  Node *p;
+  {
+    Node local;
+    p = &local;   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }               // expected-note {{local variable 'local' is destroyed here}}
+  p = ident(p);   // expected-note {{later used here}}
+}
+} // namespace opaque_callees
+
+// Reads with no lvalue-to-rvalue conversion in the AST.
+namespace class_reads {
----------------
usx95 wrote:

I am curious what are your thoughts on user-defined increment operators on view 
types.
```cpp
std::vector<int> v;
auto it = v.begin();
{
  std::vector<int> v;
  it = v.begin();
}
it++;
}
```
I think `it++` is a use as `it` passed as an argument to an opaque increment 
operator.

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

Reply via email to