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